1 /*
2  * Copyright (C) 2016 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.util;
18 
19 import android.annotation.Nullable;
20 import android.net.IpConfiguration;
21 import android.net.IpConfiguration.IpAssignment;
22 import android.net.IpConfiguration.ProxySettings;
23 import android.net.LinkAddress;
24 import android.net.MacAddress;
25 import android.net.NetworkUtils;
26 import android.net.ProxyInfo;
27 import android.net.RouteInfo;
28 import android.net.StaticIpConfiguration;
29 import android.net.Uri;
30 import android.net.wifi.WifiConfiguration;
31 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
32 import android.net.wifi.WifiEnterpriseConfig;
33 import android.text.TextUtils;
34 import android.util.Log;
35 import android.util.Pair;
36 
37 import com.android.internal.util.XmlUtils;
38 
39 import org.xmlpull.v1.XmlPullParser;
40 import org.xmlpull.v1.XmlPullParserException;
41 import org.xmlpull.v1.XmlSerializer;
42 
43 import java.io.IOException;
44 import java.net.Inet4Address;
45 import java.net.InetAddress;
46 import java.util.Arrays;
47 import java.util.BitSet;
48 import java.util.HashMap;
49 
50 /**
51  * Utils for manipulating XML data. This is essentially a wrapper over XmlUtils provided by core.
52  * The utility provides methods to write/parse section headers and write/parse values.
53  * This utility is designed for formatting the XML into the following format:
54  * <Document Header>
55  *  <Section 1 Header>
56  *   <Value 1>
57  *   <Value 2>
58  *   ...
59  *   <Sub Section 1 Header>
60  *    <Value 1>
61  *    <Value 2>
62  *    ...
63  *   </Sub Section 1 Header>
64  *  </Section 1 Header>
65  * </Document Header>
66  *
67  * Note: These utility methods are meant to be used for:
68  * 1. Backup/restore wifi network data to/from cloud.
69  * 2. Persisting wifi network data to/from disk.
70  */
71 public class XmlUtil {
72     private static final String TAG = "WifiXmlUtil";
73 
74     /**
75      * Ensure that the XML stream is at a start tag or the end of document.
76      *
77      * @throws XmlPullParserException if parsing errors occur.
78      */
gotoStartTag(XmlPullParser in)79     private static void gotoStartTag(XmlPullParser in)
80             throws XmlPullParserException, IOException {
81         int type = in.getEventType();
82         while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
83             type = in.next();
84         }
85     }
86 
87     /**
88      * Ensure that the XML stream is at an end tag or the end of document.
89      *
90      * @throws XmlPullParserException if parsing errors occur.
91      */
gotoEndTag(XmlPullParser in)92     private static void gotoEndTag(XmlPullParser in)
93             throws XmlPullParserException, IOException {
94         int type = in.getEventType();
95         while (type != XmlPullParser.END_TAG && type != XmlPullParser.END_DOCUMENT) {
96             type = in.next();
97         }
98     }
99 
100     /**
101      * Start processing the XML stream at the document header.
102      *
103      * @param in         XmlPullParser instance pointing to the XML stream.
104      * @param headerName expected name for the start tag.
105      * @throws XmlPullParserException if parsing errors occur.
106      */
gotoDocumentStart(XmlPullParser in, String headerName)107     public static void gotoDocumentStart(XmlPullParser in, String headerName)
108             throws XmlPullParserException, IOException {
109         XmlUtils.beginDocument(in, headerName);
110     }
111 
112     /**
113      * Move the XML stream to the next section header or indicate if there are no more sections.
114      * The provided outerDepth is used to find sub sections within that depth.
115      *
116      * Use this to move across sections if the ordering of sections are variable. The returned name
117      * can be used to decide what section is next.
118      *
119      * @param in         XmlPullParser instance pointing to the XML stream.
120      * @param headerName An array of one string, used to return the name of the next section.
121      * @param outerDepth Find section within this depth.
122      * @return {@code true} if a next section is found, {@code false} if there are no more sections.
123      * @throws XmlPullParserException if parsing errors occur.
124      */
gotoNextSectionOrEnd( XmlPullParser in, String[] headerName, int outerDepth)125     public static boolean gotoNextSectionOrEnd(
126             XmlPullParser in, String[] headerName, int outerDepth)
127             throws XmlPullParserException, IOException {
128         if (XmlUtils.nextElementWithin(in, outerDepth)) {
129             headerName[0] = in.getName();
130             return true;
131         }
132         return false;
133     }
134 
135     /**
136      * Move the XML stream to the next section header or indicate if there are no more sections.
137      * If a section, exists ensure that the name matches the provided name.
138      * The provided outerDepth is used to find sub sections within that depth.
139      *
140      * Use this to move across repeated sections until the end.
141      *
142      * @param in           XmlPullParser instance pointing to the XML stream.
143      * @param expectedName expected name for the section header.
144      * @param outerDepth   Find section within this depth.
145      * @return {@code true} if a next section is found, {@code false} if there are no more sections.
146      * @throws XmlPullParserException if the section header name does not match |expectedName|,
147      *                                or if parsing errors occur.
148      */
gotoNextSectionWithNameOrEnd( XmlPullParser in, String expectedName, int outerDepth)149     public static boolean gotoNextSectionWithNameOrEnd(
150             XmlPullParser in, String expectedName, int outerDepth)
151             throws XmlPullParserException, IOException {
152         String[] headerName = new String[1];
153         if (gotoNextSectionOrEnd(in, headerName, outerDepth)) {
154             if (headerName[0].equals(expectedName)) {
155                 return true;
156             }
157             throw new XmlPullParserException(
158                     "Next section name does not match expected name: " + expectedName);
159         }
160         return false;
161     }
162 
163     /**
164      * Move the XML stream to the next section header and ensure that the name matches the provided
165      * name.
166      * The provided outerDepth is used to find sub sections within that depth.
167      *
168      * Use this to move across sections if the ordering of sections are fixed.
169      *
170      * @param in           XmlPullParser instance pointing to the XML stream.
171      * @param expectedName expected name for the section header.
172      * @param outerDepth   Find section within this depth.
173      * @throws XmlPullParserException if the section header name does not match |expectedName|,
174      *                                there are no more sections or if parsing errors occur.
175      */
gotoNextSectionWithName( XmlPullParser in, String expectedName, int outerDepth)176     public static void gotoNextSectionWithName(
177             XmlPullParser in, String expectedName, int outerDepth)
178             throws XmlPullParserException, IOException {
179         if (!gotoNextSectionWithNameOrEnd(in, expectedName, outerDepth)) {
180             throw new XmlPullParserException("Section not found. Expected: " + expectedName);
181         }
182     }
183 
184     /**
185      * Checks if the stream is at the end of a section of values. This moves the stream to next tag
186      * and checks if it finds an end tag at the specified depth.
187      *
188      * @param in           XmlPullParser instance pointing to the XML stream.
189      * @param sectionDepth depth of the start tag of this section. Used to match the end tag.
190      * @return {@code true} if a end tag at the provided depth is found, {@code false} otherwise
191      * @throws XmlPullParserException if parsing errors occur.
192      */
isNextSectionEnd(XmlPullParser in, int sectionDepth)193     public static boolean isNextSectionEnd(XmlPullParser in, int sectionDepth)
194             throws XmlPullParserException, IOException {
195         return !XmlUtils.nextElementWithin(in, sectionDepth);
196     }
197 
198     /**
199      * Read the current value in the XML stream using core XmlUtils and stores the retrieved
200      * value name in the string provided. This method reads the value contained in current start
201      * tag.
202      * Note: Because there could be genuine null values being read from the XML, this method raises
203      * an exception to indicate errors.
204      *
205      * @param in        XmlPullParser instance pointing to the XML stream.
206      * @param valueName An array of one string, used to return the name attribute
207      *                  of the value's tag.
208      * @return value retrieved from the XML stream.
209      * @throws XmlPullParserException if parsing errors occur.
210      */
readCurrentValue(XmlPullParser in, String[] valueName)211     public static Object readCurrentValue(XmlPullParser in, String[] valueName)
212             throws XmlPullParserException, IOException {
213         Object value = XmlUtils.readValueXml(in, valueName);
214         // XmlUtils.readValue does not always move the stream to the end of the tag. So, move
215         // it to the end tag before returning from here.
216         gotoEndTag(in);
217         return value;
218     }
219 
220     /**
221      * Read the next value in the XML stream using core XmlUtils and ensure that it matches the
222      * provided name. This method moves the stream to the next start tag and reads the value
223      * contained in it.
224      * Note: Because there could be genuine null values being read from the XML, this method raises
225      * an exception to indicate errors.
226      *
227      * @param in XmlPullParser instance pointing to the XML stream.
228      * @return value retrieved from the XML stream.
229      * @throws XmlPullParserException if the value read does not match |expectedName|,
230      *                                or if parsing errors occur.
231      */
readNextValueWithName(XmlPullParser in, String expectedName)232     public static Object readNextValueWithName(XmlPullParser in, String expectedName)
233             throws XmlPullParserException, IOException {
234         String[] valueName = new String[1];
235         XmlUtils.nextElement(in);
236         Object value = readCurrentValue(in, valueName);
237         if (valueName[0].equals(expectedName)) {
238             return value;
239         }
240         throw new XmlPullParserException(
241                 "Value not found. Expected: " + expectedName + ", but got: " + valueName[0]);
242     }
243 
244     /**
245      * Write the XML document start with the provided document header name.
246      *
247      * @param out        XmlSerializer instance pointing to the XML stream.
248      * @param headerName name for the start tag.
249      */
writeDocumentStart(XmlSerializer out, String headerName)250     public static void writeDocumentStart(XmlSerializer out, String headerName)
251             throws IOException {
252         out.startDocument(null, true);
253         out.startTag(null, headerName);
254     }
255 
256     /**
257      * Write the XML document end with the provided document header name.
258      *
259      * @param out        XmlSerializer instance pointing to the XML stream.
260      * @param headerName name for the end tag.
261      */
writeDocumentEnd(XmlSerializer out, String headerName)262     public static void writeDocumentEnd(XmlSerializer out, String headerName)
263             throws IOException {
264         out.endTag(null, headerName);
265         out.endDocument();
266     }
267 
268     /**
269      * Write a section start header tag with the provided section name.
270      *
271      * @param out        XmlSerializer instance pointing to the XML stream.
272      * @param headerName name for the start tag.
273      */
writeNextSectionStart(XmlSerializer out, String headerName)274     public static void writeNextSectionStart(XmlSerializer out, String headerName)
275             throws IOException {
276         out.startTag(null, headerName);
277     }
278 
279     /**
280      * Write a section end header tag with the provided section name.
281      *
282      * @param out        XmlSerializer instance pointing to the XML stream.
283      * @param headerName name for the end tag.
284      */
writeNextSectionEnd(XmlSerializer out, String headerName)285     public static void writeNextSectionEnd(XmlSerializer out, String headerName)
286             throws IOException {
287         out.endTag(null, headerName);
288     }
289 
290     /**
291      * Write the value with the provided name in the XML stream using core XmlUtils.
292      *
293      * @param out   XmlSerializer instance pointing to the XML stream.
294      * @param name  name of the value.
295      * @param value value to be written.
296      */
writeNextValue(XmlSerializer out, String name, Object value)297     public static void writeNextValue(XmlSerializer out, String name, Object value)
298             throws XmlPullParserException, IOException {
299         XmlUtils.writeValueXml(value, name, out);
300     }
301 
302     /**
303      * Utility class to serialize and deserialize {@link WifiConfiguration} object to XML &
304      * vice versa.
305      * This is used by both {@link com.android.server.wifi.WifiConfigStore} &
306      * {@link com.android.server.wifi.WifiBackupRestore} modules.
307      * The |writeConfigurationToXml| has 2 versions, one for backup and one for config store.
308      * There is only 1 version of |parseXmlToConfiguration| for both backup & config store.
309      * The parse method is written so that any element added/deleted in future revisions can
310      * be easily handled.
311      */
312     public static class WifiConfigurationXmlUtil {
313         /**
314          * List of XML tags corresponding to WifiConfiguration object elements.
315          */
316         public static final String XML_TAG_SSID = "SSID";
317         public static final String XML_TAG_BSSID = "BSSID";
318         public static final String XML_TAG_CONFIG_KEY = "ConfigKey";
319         public static final String XML_TAG_PRE_SHARED_KEY = "PreSharedKey";
320         public static final String XML_TAG_WEP_KEYS = "WEPKeys";
321         public static final String XML_TAG_WEP_TX_KEY_INDEX = "WEPTxKeyIndex";
322         public static final String XML_TAG_HIDDEN_SSID = "HiddenSSID";
323         public static final String XML_TAG_REQUIRE_PMF = "RequirePMF";
324         public static final String XML_TAG_ALLOWED_KEY_MGMT = "AllowedKeyMgmt";
325         public static final String XML_TAG_ALLOWED_PROTOCOLS = "AllowedProtocols";
326         public static final String XML_TAG_ALLOWED_AUTH_ALGOS = "AllowedAuthAlgos";
327         public static final String XML_TAG_ALLOWED_GROUP_CIPHERS = "AllowedGroupCiphers";
328         public static final String XML_TAG_ALLOWED_PAIRWISE_CIPHERS = "AllowedPairwiseCiphers";
329         public static final String XML_TAG_ALLOWED_GROUP_MGMT_CIPHERS = "AllowedGroupMgmtCiphers";
330         public static final String XML_TAG_ALLOWED_SUITE_B_CIPHERS = "AllowedSuiteBCiphers";
331         public static final String XML_TAG_SHARED = "Shared";
332         public static final String XML_TAG_STATUS = "Status";
333         public static final String XML_TAG_FQDN = "FQDN";
334         public static final String XML_TAG_PROVIDER_FRIENDLY_NAME = "ProviderFriendlyName";
335         public static final String XML_TAG_LINKED_NETWORKS_LIST = "LinkedNetworksList";
336         public static final String XML_TAG_DEFAULT_GW_MAC_ADDRESS = "DefaultGwMacAddress";
337         public static final String XML_TAG_VALIDATED_INTERNET_ACCESS = "ValidatedInternetAccess";
338         public static final String XML_TAG_NO_INTERNET_ACCESS_EXPECTED = "NoInternetAccessExpected";
339         public static final String XML_TAG_USER_APPROVED = "UserApproved";
340         public static final String XML_TAG_METERED_HINT = "MeteredHint";
341         public static final String XML_TAG_METERED_OVERRIDE = "MeteredOverride";
342         public static final String XML_TAG_USE_EXTERNAL_SCORES = "UseExternalScores";
343         public static final String XML_TAG_NUM_ASSOCIATION = "NumAssociation";
344         public static final String XML_TAG_CREATOR_UID = "CreatorUid";
345         public static final String XML_TAG_CREATOR_NAME = "CreatorName";
346         public static final String XML_TAG_CREATION_TIME = "CreationTime";
347         public static final String XML_TAG_LAST_UPDATE_UID = "LastUpdateUid";
348         public static final String XML_TAG_LAST_UPDATE_NAME = "LastUpdateName";
349         public static final String XML_TAG_LAST_CONNECT_UID = "LastConnectUid";
350         public static final String XML_TAG_IS_LEGACY_PASSPOINT_CONFIG = "IsLegacyPasspointConfig";
351         public static final String XML_TAG_ROAMING_CONSORTIUM_OIS = "RoamingConsortiumOIs";
352         public static final String XML_TAG_RANDOMIZED_MAC_ADDRESS = "RandomizedMacAddress";
353         public static final String XML_TAG_MAC_RANDOMIZATION_SETTING = "MacRandomizationSetting";
354 
355         /**
356          * Write WepKeys to the XML stream.
357          * WepKeys array is intialized in WifiConfiguration constructor, but all of the elements
358          * are set to null. User may chose to set any one of the key elements in WifiConfiguration.
359          * XmlUtils serialization doesn't handle this array of nulls well .
360          * So, write empty strings if some of the keys are not initialized and null if all of
361          * the elements are empty.
362          */
writeWepKeysToXml(XmlSerializer out, String[] wepKeys)363         private static void writeWepKeysToXml(XmlSerializer out, String[] wepKeys)
364                 throws XmlPullParserException, IOException {
365             String[] wepKeysToWrite = new String[wepKeys.length];
366             boolean hasWepKey = false;
367             for (int i = 0; i < wepKeys.length; i++) {
368                 if (wepKeys[i] == null) {
369                     wepKeysToWrite[i] = new String();
370                 } else {
371                     wepKeysToWrite[i] = wepKeys[i];
372                     hasWepKey = true;
373                 }
374             }
375             if (hasWepKey) {
376                 XmlUtil.writeNextValue(out, XML_TAG_WEP_KEYS, wepKeysToWrite);
377             } else {
378                 XmlUtil.writeNextValue(out, XML_TAG_WEP_KEYS, null);
379             }
380         }
381 
382         /**
383          * Write preshared key to the XML stream.
384          *
385          * If encryptionUtil is null or if encryption fails for some reason, the pre-shared
386          * key is stored in plaintext, else the encrypted psk is stored.
387          */
writePreSharedKeyToXml( XmlSerializer out, String preSharedKey, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)388         private static void writePreSharedKeyToXml(
389                 XmlSerializer out, String preSharedKey,
390                 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
391                 throws XmlPullParserException, IOException {
392             EncryptedData encryptedData = null;
393             if (encryptionUtil != null) {
394                 if (preSharedKey != null) {
395                     encryptedData = encryptionUtil.encrypt(preSharedKey.getBytes());
396                     if (encryptedData == null) {
397                         // We silently fail encryption failures!
398                         Log.wtf(TAG, "Encryption of preSharedKey failed");
399                     }
400                 }
401             }
402             if (encryptedData != null) {
403                 XmlUtil.writeNextSectionStart(out, XML_TAG_PRE_SHARED_KEY);
404                 EncryptedDataXmlUtil.writeToXml(out, encryptedData);
405                 XmlUtil.writeNextSectionEnd(out, XML_TAG_PRE_SHARED_KEY);
406             } else {
407                 XmlUtil.writeNextValue(out, XML_TAG_PRE_SHARED_KEY, preSharedKey);
408             }
409         }
410 
411         /**
412          * Write the Configuration data elements that are common for backup & config store to the
413          * XML stream.
414          *
415          * @param out XmlSerializer instance pointing to the XML stream.
416          * @param configuration WifiConfiguration object to be serialized.
417          * @param encryptionUtil Instance of {@link EncryptedDataXmlUtil}. Backup/restore stores
418          *                       keys unencrypted.
419          */
writeCommonElementsToXml( XmlSerializer out, WifiConfiguration configuration, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)420         public static void writeCommonElementsToXml(
421                 XmlSerializer out, WifiConfiguration configuration,
422                 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
423                 throws XmlPullParserException, IOException {
424             XmlUtil.writeNextValue(out, XML_TAG_CONFIG_KEY, configuration.configKey());
425             XmlUtil.writeNextValue(out, XML_TAG_SSID, configuration.SSID);
426             XmlUtil.writeNextValue(out, XML_TAG_BSSID, configuration.BSSID);
427             writePreSharedKeyToXml(out, configuration.preSharedKey, encryptionUtil);
428             writeWepKeysToXml(out, configuration.wepKeys);
429             XmlUtil.writeNextValue(out, XML_TAG_WEP_TX_KEY_INDEX, configuration.wepTxKeyIndex);
430             XmlUtil.writeNextValue(out, XML_TAG_HIDDEN_SSID, configuration.hiddenSSID);
431             XmlUtil.writeNextValue(out, XML_TAG_REQUIRE_PMF, configuration.requirePMF);
432             XmlUtil.writeNextValue(
433                     out, XML_TAG_ALLOWED_KEY_MGMT,
434                     configuration.allowedKeyManagement.toByteArray());
435             XmlUtil.writeNextValue(
436                     out, XML_TAG_ALLOWED_PROTOCOLS,
437                     configuration.allowedProtocols.toByteArray());
438             XmlUtil.writeNextValue(
439                     out, XML_TAG_ALLOWED_AUTH_ALGOS,
440                     configuration.allowedAuthAlgorithms.toByteArray());
441             XmlUtil.writeNextValue(
442                     out, XML_TAG_ALLOWED_GROUP_CIPHERS,
443                     configuration.allowedGroupCiphers.toByteArray());
444             XmlUtil.writeNextValue(
445                     out, XML_TAG_ALLOWED_PAIRWISE_CIPHERS,
446                     configuration.allowedPairwiseCiphers.toByteArray());
447             XmlUtil.writeNextValue(
448                     out, XML_TAG_ALLOWED_GROUP_MGMT_CIPHERS,
449                     configuration.allowedGroupManagementCiphers.toByteArray());
450             XmlUtil.writeNextValue(
451                     out, XML_TAG_ALLOWED_SUITE_B_CIPHERS,
452                     configuration.allowedSuiteBCiphers.toByteArray());
453             XmlUtil.writeNextValue(out, XML_TAG_SHARED, configuration.shared);
454         }
455 
456         /**
457          * Write the Configuration data elements for backup from the provided Configuration to the
458          * XML stream.
459          * Note: This is a subset of the elements serialized for config store.
460          *
461          * @param out           XmlSerializer instance pointing to the XML stream.
462          * @param configuration WifiConfiguration object to be serialized.
463          */
writeToXmlForBackup(XmlSerializer out, WifiConfiguration configuration)464         public static void writeToXmlForBackup(XmlSerializer out, WifiConfiguration configuration)
465                 throws XmlPullParserException, IOException {
466             writeCommonElementsToXml(out, configuration, null);
467             XmlUtil.writeNextValue(out, XML_TAG_METERED_OVERRIDE, configuration.meteredOverride);
468         }
469 
470         /**
471          * Write the Configuration data elements for config store from the provided Configuration
472          * to the XML stream.
473          *
474          * @param out XmlSerializer instance pointing to the XML stream.
475          * @param configuration WifiConfiguration object to be serialized.
476          * @param encryptionUtil Instance of {@link EncryptedDataXmlUtil}.
477          */
writeToXmlForConfigStore( XmlSerializer out, WifiConfiguration configuration, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)478         public static void writeToXmlForConfigStore(
479                 XmlSerializer out, WifiConfiguration configuration,
480                 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
481                 throws XmlPullParserException, IOException {
482             writeCommonElementsToXml(out, configuration, encryptionUtil);
483             XmlUtil.writeNextValue(out, XML_TAG_STATUS, configuration.status);
484             XmlUtil.writeNextValue(out, XML_TAG_FQDN, configuration.FQDN);
485             XmlUtil.writeNextValue(
486                     out, XML_TAG_PROVIDER_FRIENDLY_NAME, configuration.providerFriendlyName);
487             XmlUtil.writeNextValue(
488                     out, XML_TAG_LINKED_NETWORKS_LIST, configuration.linkedConfigurations);
489             XmlUtil.writeNextValue(
490                     out, XML_TAG_DEFAULT_GW_MAC_ADDRESS, configuration.defaultGwMacAddress);
491             XmlUtil.writeNextValue(
492                     out, XML_TAG_VALIDATED_INTERNET_ACCESS, configuration.validatedInternetAccess);
493             XmlUtil.writeNextValue(
494                     out, XML_TAG_NO_INTERNET_ACCESS_EXPECTED,
495                     configuration.noInternetAccessExpected);
496             XmlUtil.writeNextValue(out, XML_TAG_USER_APPROVED, configuration.userApproved);
497             XmlUtil.writeNextValue(out, XML_TAG_METERED_HINT, configuration.meteredHint);
498             XmlUtil.writeNextValue(out, XML_TAG_METERED_OVERRIDE, configuration.meteredOverride);
499             XmlUtil.writeNextValue(
500                     out, XML_TAG_USE_EXTERNAL_SCORES, configuration.useExternalScores);
501             XmlUtil.writeNextValue(out, XML_TAG_NUM_ASSOCIATION, configuration.numAssociation);
502             XmlUtil.writeNextValue(out, XML_TAG_CREATOR_UID, configuration.creatorUid);
503             XmlUtil.writeNextValue(out, XML_TAG_CREATOR_NAME, configuration.creatorName);
504             XmlUtil.writeNextValue(out, XML_TAG_CREATION_TIME, configuration.creationTime);
505             XmlUtil.writeNextValue(out, XML_TAG_LAST_UPDATE_UID, configuration.lastUpdateUid);
506             XmlUtil.writeNextValue(out, XML_TAG_LAST_UPDATE_NAME, configuration.lastUpdateName);
507             XmlUtil.writeNextValue(out, XML_TAG_LAST_CONNECT_UID, configuration.lastConnectUid);
508             XmlUtil.writeNextValue(
509                     out, XML_TAG_IS_LEGACY_PASSPOINT_CONFIG,
510                     configuration.isLegacyPasspointConfig);
511             XmlUtil.writeNextValue(
512                     out, XML_TAG_ROAMING_CONSORTIUM_OIS, configuration.roamingConsortiumIds);
513             XmlUtil.writeNextValue(out, XML_TAG_RANDOMIZED_MAC_ADDRESS,
514                     configuration.getRandomizedMacAddress().toString());
515             XmlUtil.writeNextValue(out, XML_TAG_MAC_RANDOMIZATION_SETTING,
516                     configuration.macRandomizationSetting);
517         }
518 
519         /**
520          * Populate wepKeys array elements only if they were non-empty in the backup data.
521          *
522          * @throws XmlPullParserException if parsing errors occur.
523          */
populateWepKeysFromXmlValue(Object value, String[] wepKeys)524         private static void populateWepKeysFromXmlValue(Object value, String[] wepKeys)
525                 throws XmlPullParserException, IOException {
526             String[] wepKeysInData = (String[]) value;
527             if (wepKeysInData == null) {
528                 return;
529             }
530             if (wepKeysInData.length != wepKeys.length) {
531                 throw new XmlPullParserException(
532                         "Invalid Wep Keys length: " + wepKeysInData.length);
533             }
534             for (int i = 0; i < wepKeys.length; i++) {
535                 if (wepKeysInData[i].isEmpty()) {
536                     wepKeys[i] = null;
537                 } else {
538                     wepKeys[i] = wepKeysInData[i];
539                 }
540             }
541         }
542 
543         /**
544          * Parses the configuration data elements from the provided XML stream to a
545          * WifiConfiguration object.
546          * Note: This is used for parsing both backup data and config store data. Looping through
547          * the tags make it easy to add or remove elements in the future versions if needed.
548          *
549          * @param in XmlPullParser instance pointing to the XML stream.
550          * @param outerTagDepth depth of the outer tag in the XML document.
551          * @param shouldExpectEncryptedCredentials Whether to expect encrypted credentials or not.
552          * @param encryptionUtil Instance of {@link EncryptedDataXmlUtil}.
553          * @return Pair<Config key, WifiConfiguration object> if parsing is successful,
554          * null otherwise.
555          */
parseFromXml( XmlPullParser in, int outerTagDepth, boolean shouldExpectEncryptedCredentials, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)556         public static Pair<String, WifiConfiguration> parseFromXml(
557                 XmlPullParser in, int outerTagDepth, boolean shouldExpectEncryptedCredentials,
558                 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
559                 throws XmlPullParserException, IOException {
560             WifiConfiguration configuration = new WifiConfiguration();
561             String configKeyInData = null;
562             boolean macRandomizationSettingExists = false;
563 
564             // Loop through and parse out all the elements from the stream within this section.
565             while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
566                 if (in.getAttributeValue(null, "name") != null) {
567                     // Value elements.
568                     String[] valueName = new String[1];
569                     Object value = XmlUtil.readCurrentValue(in, valueName);
570                     if (valueName[0] == null) {
571                         throw new XmlPullParserException("Missing value name");
572                     }
573                     switch (valueName[0]) {
574                         case XML_TAG_CONFIG_KEY:
575                             configKeyInData = (String) value;
576                             break;
577                         case XML_TAG_SSID:
578                             configuration.SSID = (String) value;
579                             break;
580                         case XML_TAG_BSSID:
581                             configuration.BSSID = (String) value;
582                             break;
583                         case XML_TAG_PRE_SHARED_KEY:
584                             configuration.preSharedKey = (String) value;
585                             break;
586                         case XML_TAG_WEP_KEYS:
587                             populateWepKeysFromXmlValue(value, configuration.wepKeys);
588                             break;
589                         case XML_TAG_WEP_TX_KEY_INDEX:
590                             configuration.wepTxKeyIndex = (int) value;
591                             break;
592                         case XML_TAG_HIDDEN_SSID:
593                             configuration.hiddenSSID = (boolean) value;
594                             break;
595                         case XML_TAG_REQUIRE_PMF:
596                             configuration.requirePMF = (boolean) value;
597                             break;
598                         case XML_TAG_ALLOWED_KEY_MGMT:
599                             byte[] allowedKeyMgmt = (byte[]) value;
600                             configuration.allowedKeyManagement = BitSet.valueOf(allowedKeyMgmt);
601                             break;
602                         case XML_TAG_ALLOWED_PROTOCOLS:
603                             byte[] allowedProtocols = (byte[]) value;
604                             configuration.allowedProtocols = BitSet.valueOf(allowedProtocols);
605                             break;
606                         case XML_TAG_ALLOWED_AUTH_ALGOS:
607                             byte[] allowedAuthAlgorithms = (byte[]) value;
608                             configuration.allowedAuthAlgorithms = BitSet.valueOf(
609                                     allowedAuthAlgorithms);
610                             break;
611                         case XML_TAG_ALLOWED_GROUP_CIPHERS:
612                             byte[] allowedGroupCiphers = (byte[]) value;
613                             configuration.allowedGroupCiphers = BitSet.valueOf(allowedGroupCiphers);
614                             break;
615                         case XML_TAG_ALLOWED_PAIRWISE_CIPHERS:
616                             byte[] allowedPairwiseCiphers = (byte[]) value;
617                             configuration.allowedPairwiseCiphers =
618                                     BitSet.valueOf(allowedPairwiseCiphers);
619                             break;
620                         case XML_TAG_ALLOWED_GROUP_MGMT_CIPHERS:
621                             byte[] allowedGroupMgmtCiphers = (byte[]) value;
622                             configuration.allowedGroupManagementCiphers =
623                                     BitSet.valueOf(allowedGroupMgmtCiphers);
624                             break;
625                         case XML_TAG_ALLOWED_SUITE_B_CIPHERS:
626                             byte[] allowedSuiteBCiphers = (byte[]) value;
627                             configuration.allowedSuiteBCiphers =
628                                     BitSet.valueOf(allowedSuiteBCiphers);
629                             break;
630                         case XML_TAG_SHARED:
631                             configuration.shared = (boolean) value;
632                             break;
633                         case XML_TAG_STATUS:
634                             int status = (int) value;
635                             // Any network which was CURRENT before reboot needs
636                             // to be restored to ENABLED.
637                             if (status == WifiConfiguration.Status.CURRENT) {
638                                 status = WifiConfiguration.Status.ENABLED;
639                             }
640                             configuration.status = status;
641                             break;
642                         case XML_TAG_FQDN:
643                             configuration.FQDN = (String) value;
644                             break;
645                         case XML_TAG_PROVIDER_FRIENDLY_NAME:
646                             configuration.providerFriendlyName = (String) value;
647                             break;
648                         case XML_TAG_LINKED_NETWORKS_LIST:
649                             configuration.linkedConfigurations = (HashMap<String, Integer>) value;
650                             break;
651                         case XML_TAG_DEFAULT_GW_MAC_ADDRESS:
652                             configuration.defaultGwMacAddress = (String) value;
653                             break;
654                         case XML_TAG_VALIDATED_INTERNET_ACCESS:
655                             configuration.validatedInternetAccess = (boolean) value;
656                             break;
657                         case XML_TAG_NO_INTERNET_ACCESS_EXPECTED:
658                             configuration.noInternetAccessExpected = (boolean) value;
659                             break;
660                         case XML_TAG_USER_APPROVED:
661                             configuration.userApproved = (int) value;
662                             break;
663                         case XML_TAG_METERED_HINT:
664                             configuration.meteredHint = (boolean) value;
665                             break;
666                         case XML_TAG_METERED_OVERRIDE:
667                             configuration.meteredOverride = (int) value;
668                             break;
669                         case XML_TAG_USE_EXTERNAL_SCORES:
670                             configuration.useExternalScores = (boolean) value;
671                             break;
672                         case XML_TAG_NUM_ASSOCIATION:
673                             configuration.numAssociation = (int) value;
674                             break;
675                         case XML_TAG_CREATOR_UID:
676                             configuration.creatorUid = (int) value;
677                             break;
678                         case XML_TAG_CREATOR_NAME:
679                             configuration.creatorName = (String) value;
680                             break;
681                         case XML_TAG_CREATION_TIME:
682                             configuration.creationTime = (String) value;
683                             break;
684                         case XML_TAG_LAST_UPDATE_UID:
685                             configuration.lastUpdateUid = (int) value;
686                             break;
687                         case XML_TAG_LAST_UPDATE_NAME:
688                             configuration.lastUpdateName = (String) value;
689                             break;
690                         case XML_TAG_LAST_CONNECT_UID:
691                             configuration.lastConnectUid = (int) value;
692                             break;
693                         case XML_TAG_IS_LEGACY_PASSPOINT_CONFIG:
694                             configuration.isLegacyPasspointConfig = (boolean) value;
695                             break;
696                         case XML_TAG_ROAMING_CONSORTIUM_OIS:
697                             configuration.roamingConsortiumIds = (long[]) value;
698                             break;
699                         case XML_TAG_RANDOMIZED_MAC_ADDRESS:
700                             configuration.setRandomizedMacAddress(
701                                     MacAddress.fromString((String) value));
702                             break;
703                         case XML_TAG_MAC_RANDOMIZATION_SETTING:
704                             configuration.macRandomizationSetting = (int) value;
705                             macRandomizationSettingExists = true;
706                             break;
707                         default:
708                             throw new XmlPullParserException(
709                                   "Unknown value name found: " + valueName[0]);
710                     }
711                 } else {
712                     String tagName = in.getName();
713                     if (tagName == null) {
714                         throw new XmlPullParserException("Unexpected null tag found");
715                     }
716                     switch (tagName) {
717                         case XML_TAG_PRE_SHARED_KEY:
718                             if (!shouldExpectEncryptedCredentials || encryptionUtil == null) {
719                                 throw new XmlPullParserException(
720                                         "Encrypted preSharedKey section not expected");
721                             }
722                             EncryptedData encryptedData =
723                                     EncryptedDataXmlUtil.parseFromXml(in, outerTagDepth + 1);
724                             byte[] preSharedKeyBytes = encryptionUtil.decrypt(encryptedData);
725                             if (preSharedKeyBytes == null) {
726                                 Log.wtf(TAG, "Decryption of preSharedKey failed");
727                             } else {
728                                 configuration.preSharedKey = new String(preSharedKeyBytes);
729                             }
730                             break;
731                         default:
732                             throw new XmlPullParserException(
733                                   "Unknown tag name found: " + tagName);
734                     }
735                 }
736             }
737             if (!macRandomizationSettingExists) {
738                 configuration.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
739             }
740             return Pair.create(configKeyInData, configuration);
741         }
742     }
743 
744     /**
745      * Utility class to serialize and deseriaize {@link IpConfiguration} object to XML & vice versa.
746      * This is used by both {@link com.android.server.wifi.WifiConfigStore} &
747      * {@link com.android.server.wifi.WifiBackupRestore} modules.
748      */
749     public static class IpConfigurationXmlUtil {
750 
751         /**
752          * List of XML tags corresponding to IpConfiguration object elements.
753          */
754         public static final String XML_TAG_IP_ASSIGNMENT = "IpAssignment";
755         public static final String XML_TAG_LINK_ADDRESS = "LinkAddress";
756         public static final String XML_TAG_LINK_PREFIX_LENGTH = "LinkPrefixLength";
757         public static final String XML_TAG_GATEWAY_ADDRESS = "GatewayAddress";
758         public static final String XML_TAG_DNS_SERVER_ADDRESSES = "DNSServers";
759         public static final String XML_TAG_PROXY_SETTINGS = "ProxySettings";
760         public static final String XML_TAG_PROXY_HOST = "ProxyHost";
761         public static final String XML_TAG_PROXY_PORT = "ProxyPort";
762         public static final String XML_TAG_PROXY_PAC_FILE = "ProxyPac";
763         public static final String XML_TAG_PROXY_EXCLUSION_LIST = "ProxyExclusionList";
764 
765         /**
766          * Write the static IP configuration data elements to XML stream.
767          */
writeStaticIpConfigurationToXml( XmlSerializer out, StaticIpConfiguration staticIpConfiguration)768         private static void writeStaticIpConfigurationToXml(
769                 XmlSerializer out, StaticIpConfiguration staticIpConfiguration)
770                 throws XmlPullParserException, IOException {
771             if (staticIpConfiguration.ipAddress != null) {
772                 XmlUtil.writeNextValue(
773                         out, XML_TAG_LINK_ADDRESS,
774                         staticIpConfiguration.ipAddress.getAddress().getHostAddress());
775                 XmlUtil.writeNextValue(
776                         out, XML_TAG_LINK_PREFIX_LENGTH,
777                         staticIpConfiguration.ipAddress.getPrefixLength());
778             } else {
779                 XmlUtil.writeNextValue(
780                         out, XML_TAG_LINK_ADDRESS, null);
781                 XmlUtil.writeNextValue(
782                         out, XML_TAG_LINK_PREFIX_LENGTH, null);
783             }
784             if (staticIpConfiguration.gateway != null) {
785                 XmlUtil.writeNextValue(
786                         out, XML_TAG_GATEWAY_ADDRESS,
787                         staticIpConfiguration.gateway.getHostAddress());
788             } else {
789                 XmlUtil.writeNextValue(
790                         out, XML_TAG_GATEWAY_ADDRESS, null);
791 
792             }
793             if (staticIpConfiguration.dnsServers != null) {
794                 // Create a string array of DNS server addresses
795                 String[] dnsServers = new String[staticIpConfiguration.dnsServers.size()];
796                 int dnsServerIdx = 0;
797                 for (InetAddress inetAddr : staticIpConfiguration.dnsServers) {
798                     dnsServers[dnsServerIdx++] = inetAddr.getHostAddress();
799                 }
800                 XmlUtil.writeNextValue(
801                         out, XML_TAG_DNS_SERVER_ADDRESSES, dnsServers);
802             } else {
803                 XmlUtil.writeNextValue(
804                         out, XML_TAG_DNS_SERVER_ADDRESSES, null);
805             }
806         }
807 
808         /**
809          * Write the IP configuration data elements from the provided Configuration to the XML
810          * stream.
811          *
812          * @param out             XmlSerializer instance pointing to the XML stream.
813          * @param ipConfiguration IpConfiguration object to be serialized.
814          */
writeToXml(XmlSerializer out, IpConfiguration ipConfiguration)815         public static void writeToXml(XmlSerializer out, IpConfiguration ipConfiguration)
816                 throws XmlPullParserException, IOException {
817             // Write IP assignment settings
818             XmlUtil.writeNextValue(out, XML_TAG_IP_ASSIGNMENT,
819                     ipConfiguration.ipAssignment.toString());
820             switch (ipConfiguration.ipAssignment) {
821                 case STATIC:
822                     writeStaticIpConfigurationToXml(
823                             out, ipConfiguration.getStaticIpConfiguration());
824                     break;
825                 default:
826                     break;
827             }
828 
829             // Write proxy settings
830             XmlUtil.writeNextValue(
831                     out, XML_TAG_PROXY_SETTINGS,
832                     ipConfiguration.proxySettings.toString());
833             switch (ipConfiguration.proxySettings) {
834                 case STATIC:
835                     XmlUtil.writeNextValue(
836                             out, XML_TAG_PROXY_HOST,
837                             ipConfiguration.httpProxy.getHost());
838                     XmlUtil.writeNextValue(
839                             out, XML_TAG_PROXY_PORT,
840                             ipConfiguration.httpProxy.getPort());
841                     XmlUtil.writeNextValue(
842                             out, XML_TAG_PROXY_EXCLUSION_LIST,
843                             ipConfiguration.httpProxy.getExclusionListAsString());
844                     break;
845                 case PAC:
846                     XmlUtil.writeNextValue(
847                             out, XML_TAG_PROXY_PAC_FILE,
848                             ipConfiguration.httpProxy.getPacFileUrl().toString());
849                     break;
850                 default:
851                     break;
852             }
853         }
854 
855         /**
856          * Parse out the static IP configuration from the XML stream.
857          */
parseStaticIpConfigurationFromXml(XmlPullParser in)858         private static StaticIpConfiguration parseStaticIpConfigurationFromXml(XmlPullParser in)
859                 throws XmlPullParserException, IOException {
860             StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
861 
862             String linkAddressString =
863                     (String) XmlUtil.readNextValueWithName(in, XML_TAG_LINK_ADDRESS);
864             Integer linkPrefixLength =
865                     (Integer) XmlUtil.readNextValueWithName(in, XML_TAG_LINK_PREFIX_LENGTH);
866             if (linkAddressString != null && linkPrefixLength != null) {
867                 LinkAddress linkAddress = new LinkAddress(
868                         NetworkUtils.numericToInetAddress(linkAddressString),
869                         linkPrefixLength);
870                 if (linkAddress.getAddress() instanceof Inet4Address) {
871                     staticIpConfiguration.ipAddress = linkAddress;
872                 } else {
873                     Log.w(TAG, "Non-IPv4 address: " + linkAddress);
874                 }
875             }
876             String gatewayAddressString =
877                     (String) XmlUtil.readNextValueWithName(in, XML_TAG_GATEWAY_ADDRESS);
878             if (gatewayAddressString != null) {
879                 LinkAddress dest = null;
880                 InetAddress gateway =
881                         NetworkUtils.numericToInetAddress(gatewayAddressString);
882                 RouteInfo route = new RouteInfo(dest, gateway);
883                 if (route.isIPv4Default()) {
884                     staticIpConfiguration.gateway = gateway;
885                 } else {
886                     Log.w(TAG, "Non-IPv4 default route: " + route);
887                 }
888             }
889             String[] dnsServerAddressesString =
890                     (String[]) XmlUtil.readNextValueWithName(in, XML_TAG_DNS_SERVER_ADDRESSES);
891             if (dnsServerAddressesString != null) {
892                 for (String dnsServerAddressString : dnsServerAddressesString) {
893                     InetAddress dnsServerAddress =
894                             NetworkUtils.numericToInetAddress(dnsServerAddressString);
895                     staticIpConfiguration.dnsServers.add(dnsServerAddress);
896                 }
897             }
898             return staticIpConfiguration;
899         }
900 
901         /**
902          * Parses the IP configuration data elements from the provided XML stream to an
903          * IpConfiguration object.
904          *
905          * @param in            XmlPullParser instance pointing to the XML stream.
906          * @param outerTagDepth depth of the outer tag in the XML document.
907          * @return IpConfiguration object if parsing is successful, null otherwise.
908          */
parseFromXml(XmlPullParser in, int outerTagDepth)909         public static IpConfiguration parseFromXml(XmlPullParser in, int outerTagDepth)
910                 throws XmlPullParserException, IOException {
911             IpConfiguration ipConfiguration = new IpConfiguration();
912 
913             // Parse out the IP assignment info first.
914             String ipAssignmentString =
915                     (String) XmlUtil.readNextValueWithName(in, XML_TAG_IP_ASSIGNMENT);
916             IpAssignment ipAssignment = IpAssignment.valueOf(ipAssignmentString);
917             ipConfiguration.setIpAssignment(ipAssignment);
918             switch (ipAssignment) {
919                 case STATIC:
920                     ipConfiguration.setStaticIpConfiguration(parseStaticIpConfigurationFromXml(in));
921                     break;
922                 case DHCP:
923                 case UNASSIGNED:
924                     break;
925                 default:
926                     throw new XmlPullParserException("Unknown ip assignment type: " + ipAssignment);
927             }
928 
929             // Parse out the proxy settings next.
930             String proxySettingsString =
931                     (String) XmlUtil.readNextValueWithName(in, XML_TAG_PROXY_SETTINGS);
932             ProxySettings proxySettings = ProxySettings.valueOf(proxySettingsString);
933             ipConfiguration.setProxySettings(proxySettings);
934             switch (proxySettings) {
935                 case STATIC:
936                     String proxyHost =
937                             (String) XmlUtil.readNextValueWithName(in, XML_TAG_PROXY_HOST);
938                     int proxyPort =
939                             (int) XmlUtil.readNextValueWithName(in, XML_TAG_PROXY_PORT);
940                     String proxyExclusionList =
941                             (String) XmlUtil.readNextValueWithName(
942                                     in, XML_TAG_PROXY_EXCLUSION_LIST);
943                     ipConfiguration.setHttpProxy(
944                             new ProxyInfo(proxyHost, proxyPort, proxyExclusionList));
945                     break;
946                 case PAC:
947                     String proxyPacFile =
948                             (String) XmlUtil.readNextValueWithName(in, XML_TAG_PROXY_PAC_FILE);
949                     ipConfiguration.setHttpProxy(
950                             ProxyInfo.buildPacProxy(Uri.parse(proxyPacFile)));
951                     break;
952                 case NONE:
953                 case UNASSIGNED:
954                     break;
955                 default:
956                     throw new XmlPullParserException(
957                             "Unknown proxy settings type: " + proxySettings);
958             }
959             return ipConfiguration;
960         }
961     }
962 
963     /**
964      * Utility class to serialize and deseriaize {@link NetworkSelectionStatus} object to XML &
965      * vice versa. This is used by {@link com.android.server.wifi.WifiConfigStore} module.
966      */
967     public static class NetworkSelectionStatusXmlUtil {
968 
969         /**
970          * List of XML tags corresponding to NetworkSelectionStatus object elements.
971          */
972         public static final String XML_TAG_SELECTION_STATUS = "SelectionStatus";
973         public static final String XML_TAG_DISABLE_REASON = "DisableReason";
974         public static final String XML_TAG_CONNECT_CHOICE = "ConnectChoice";
975         public static final String XML_TAG_CONNECT_CHOICE_TIMESTAMP = "ConnectChoiceTimeStamp";
976         public static final String XML_TAG_HAS_EVER_CONNECTED = "HasEverConnected";
977 
978         /**
979          * Write the NetworkSelectionStatus data elements from the provided status to the XML
980          * stream.
981          *
982          * @param out             XmlSerializer instance pointing to the XML stream.
983          * @param selectionStatus NetworkSelectionStatus object to be serialized.
984          */
writeToXml(XmlSerializer out, NetworkSelectionStatus selectionStatus)985         public static void writeToXml(XmlSerializer out, NetworkSelectionStatus selectionStatus)
986                 throws XmlPullParserException, IOException {
987             XmlUtil.writeNextValue(
988                     out, XML_TAG_SELECTION_STATUS, selectionStatus.getNetworkStatusString());
989             XmlUtil.writeNextValue(
990                     out, XML_TAG_DISABLE_REASON, selectionStatus.getNetworkDisableReasonString());
991             XmlUtil.writeNextValue(out, XML_TAG_CONNECT_CHOICE, selectionStatus.getConnectChoice());
992             XmlUtil.writeNextValue(
993                     out, XML_TAG_CONNECT_CHOICE_TIMESTAMP,
994                     selectionStatus.getConnectChoiceTimestamp());
995             XmlUtil.writeNextValue(
996                     out, XML_TAG_HAS_EVER_CONNECTED, selectionStatus.getHasEverConnected());
997         }
998 
999         /**
1000          * Parses the NetworkSelectionStatus data elements from the provided XML stream to a
1001          * NetworkSelectionStatus object.
1002          *
1003          * @param in            XmlPullParser instance pointing to the XML stream.
1004          * @param outerTagDepth depth of the outer tag in the XML document.
1005          * @return NetworkSelectionStatus object if parsing is successful, null otherwise.
1006          */
parseFromXml(XmlPullParser in, int outerTagDepth)1007         public static NetworkSelectionStatus parseFromXml(XmlPullParser in, int outerTagDepth)
1008                 throws XmlPullParserException, IOException {
1009             NetworkSelectionStatus selectionStatus = new NetworkSelectionStatus();
1010             String statusString = "";
1011             String disableReasonString = "";
1012 
1013             // Loop through and parse out all the elements from the stream within this section.
1014             while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
1015                 String[] valueName = new String[1];
1016                 Object value = XmlUtil.readCurrentValue(in, valueName);
1017                 if (valueName[0] == null) {
1018                     throw new XmlPullParserException("Missing value name");
1019                 }
1020                 switch (valueName[0]) {
1021                     case XML_TAG_SELECTION_STATUS:
1022                         statusString = (String) value;
1023                         break;
1024                     case XML_TAG_DISABLE_REASON:
1025                         disableReasonString = (String) value;
1026                         break;
1027                     case XML_TAG_CONNECT_CHOICE:
1028                         selectionStatus.setConnectChoice((String) value);
1029                         break;
1030                     case XML_TAG_CONNECT_CHOICE_TIMESTAMP:
1031                         selectionStatus.setConnectChoiceTimestamp((long) value);
1032                         break;
1033                     case XML_TAG_HAS_EVER_CONNECTED:
1034                         selectionStatus.setHasEverConnected((boolean) value);
1035                         break;
1036                     default:
1037                         throw new XmlPullParserException(
1038                                 "Unknown value name found: " + valueName[0]);
1039                 }
1040             }
1041             // Now figure out the network selection status codes from |selectionStatusString| &
1042             // |disableReasonString|.
1043             int status =
1044                     Arrays.asList(NetworkSelectionStatus.QUALITY_NETWORK_SELECTION_STATUS)
1045                             .indexOf(statusString);
1046             int disableReason =
1047                     Arrays.asList(NetworkSelectionStatus.QUALITY_NETWORK_SELECTION_DISABLE_REASON)
1048                             .indexOf(disableReasonString);
1049 
1050             // If either of the above codes are invalid or if the network was temporarily disabled
1051             // (blacklisted), restore the status as enabled. We don't want to persist blacklists
1052             // across reboots.
1053             if (status == -1 || disableReason == -1 ||
1054                     status == NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED) {
1055                 status = NetworkSelectionStatus.NETWORK_SELECTION_ENABLED;
1056                 disableReason = NetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
1057             }
1058             selectionStatus.setNetworkSelectionStatus(status);
1059             selectionStatus.setNetworkSelectionDisableReason(disableReason);
1060             return selectionStatus;
1061         }
1062     }
1063 
1064     /**
1065      * Utility class to serialize and deseriaize {@link WifiEnterpriseConfig} object to XML &
1066      * vice versa. This is used by {@link com.android.server.wifi.WifiConfigStore} module.
1067      */
1068     public static class WifiEnterpriseConfigXmlUtil {
1069 
1070         /**
1071          * List of XML tags corresponding to WifiEnterpriseConfig object elements.
1072          */
1073         public static final String XML_TAG_IDENTITY = "Identity";
1074         public static final String XML_TAG_ANON_IDENTITY = "AnonIdentity";
1075         public static final String XML_TAG_PASSWORD = "Password";
1076         public static final String XML_TAG_CLIENT_CERT = "ClientCert";
1077         public static final String XML_TAG_CA_CERT = "CaCert";
1078         public static final String XML_TAG_SUBJECT_MATCH = "SubjectMatch";
1079         public static final String XML_TAG_ENGINE = "Engine";
1080         public static final String XML_TAG_ENGINE_ID = "EngineId";
1081         public static final String XML_TAG_PRIVATE_KEY_ID = "PrivateKeyId";
1082         public static final String XML_TAG_ALT_SUBJECT_MATCH = "AltSubjectMatch";
1083         public static final String XML_TAG_DOM_SUFFIX_MATCH = "DomSuffixMatch";
1084         public static final String XML_TAG_CA_PATH = "CaPath";
1085         public static final String XML_TAG_EAP_METHOD = "EapMethod";
1086         public static final String XML_TAG_PHASE2_METHOD = "Phase2Method";
1087         public static final String XML_TAG_PLMN = "PLMN";
1088         public static final String XML_TAG_REALM = "Realm";
1089 
1090         /**
1091          * Write password key to the XML stream.
1092          *
1093          * If encryptionUtil is null or if encryption fails for some reason, the password is stored
1094          * in plaintext, else the encrypted psk is stored.
1095          */
writePasswordToXml( XmlSerializer out, String password, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)1096         private static void writePasswordToXml(
1097                 XmlSerializer out, String password,
1098                 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
1099                 throws XmlPullParserException, IOException {
1100             EncryptedData encryptedData = null;
1101             if (encryptionUtil != null) {
1102                 if (password != null) {
1103                     encryptedData = encryptionUtil.encrypt(password.getBytes());
1104                     if (encryptedData == null) {
1105                         // We silently fail encryption failures!
1106                         Log.wtf(TAG, "Encryption of password failed");
1107                     }
1108                 }
1109             }
1110             if (encryptedData != null) {
1111                 XmlUtil.writeNextSectionStart(out, XML_TAG_PASSWORD);
1112                 EncryptedDataXmlUtil.writeToXml(out, encryptedData);
1113                 XmlUtil.writeNextSectionEnd(out, XML_TAG_PASSWORD);
1114             } else {
1115                 XmlUtil.writeNextValue(out, XML_TAG_PASSWORD, password);
1116             }
1117         }
1118 
1119         /**
1120          * Write the WifiEnterpriseConfig data elements from the provided config to the XML
1121          * stream.
1122          *
1123          * @param out XmlSerializer instance pointing to the XML stream.
1124          * @param enterpriseConfig WifiEnterpriseConfig object to be serialized.
1125          * @param encryptionUtil Instance of {@link EncryptedDataXmlUtil}.
1126          */
writeToXml(XmlSerializer out, WifiEnterpriseConfig enterpriseConfig, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)1127         public static void writeToXml(XmlSerializer out, WifiEnterpriseConfig enterpriseConfig,
1128                 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
1129                 throws XmlPullParserException, IOException {
1130             XmlUtil.writeNextValue(out, XML_TAG_IDENTITY,
1131                     enterpriseConfig.getFieldValue(WifiEnterpriseConfig.IDENTITY_KEY));
1132             XmlUtil.writeNextValue(out, XML_TAG_ANON_IDENTITY,
1133                     enterpriseConfig.getFieldValue(WifiEnterpriseConfig.ANON_IDENTITY_KEY));
1134             writePasswordToXml(
1135                     out, enterpriseConfig.getFieldValue(WifiEnterpriseConfig.PASSWORD_KEY),
1136                     encryptionUtil);
1137             XmlUtil.writeNextValue(out, XML_TAG_CLIENT_CERT,
1138                     enterpriseConfig.getFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY));
1139             XmlUtil.writeNextValue(out, XML_TAG_CA_CERT,
1140                     enterpriseConfig.getFieldValue(WifiEnterpriseConfig.CA_CERT_KEY));
1141             XmlUtil.writeNextValue(out, XML_TAG_SUBJECT_MATCH,
1142                     enterpriseConfig.getFieldValue(WifiEnterpriseConfig.SUBJECT_MATCH_KEY));
1143             XmlUtil.writeNextValue(out, XML_TAG_ENGINE,
1144                     enterpriseConfig.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY));
1145             XmlUtil.writeNextValue(out, XML_TAG_ENGINE_ID,
1146                     enterpriseConfig.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY));
1147             XmlUtil.writeNextValue(out, XML_TAG_PRIVATE_KEY_ID,
1148                     enterpriseConfig.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY));
1149             XmlUtil.writeNextValue(out, XML_TAG_ALT_SUBJECT_MATCH,
1150                     enterpriseConfig.getFieldValue(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY));
1151             XmlUtil.writeNextValue(out, XML_TAG_DOM_SUFFIX_MATCH,
1152                     enterpriseConfig.getFieldValue(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY));
1153             XmlUtil.writeNextValue(out, XML_TAG_CA_PATH,
1154                     enterpriseConfig.getFieldValue(WifiEnterpriseConfig.CA_PATH_KEY));
1155             XmlUtil.writeNextValue(out, XML_TAG_EAP_METHOD, enterpriseConfig.getEapMethod());
1156             XmlUtil.writeNextValue(out, XML_TAG_PHASE2_METHOD, enterpriseConfig.getPhase2Method());
1157             XmlUtil.writeNextValue(out, XML_TAG_PLMN, enterpriseConfig.getPlmn());
1158             XmlUtil.writeNextValue(out, XML_TAG_REALM, enterpriseConfig.getRealm());
1159         }
1160 
1161         /**
1162          * Parses the data elements from the provided XML stream to a WifiEnterpriseConfig object.
1163          *
1164          * @param in XmlPullParser instance pointing to the XML stream.
1165          * @param outerTagDepth depth of the outer tag in the XML document.
1166          * @param shouldExpectEncryptedCredentials Whether to expect encrypted credentials or not.
1167          * @param encryptionUtil Instance of {@link EncryptedDataXmlUtil}.
1168          * @return WifiEnterpriseConfig object if parsing is successful, null otherwise.
1169          */
parseFromXml(XmlPullParser in, int outerTagDepth, boolean shouldExpectEncryptedCredentials, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)1170         public static WifiEnterpriseConfig parseFromXml(XmlPullParser in, int outerTagDepth,
1171                 boolean shouldExpectEncryptedCredentials,
1172                 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
1173                 throws XmlPullParserException, IOException {
1174             WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
1175 
1176             // Loop through and parse out all the elements from the stream within this section.
1177             while (XmlUtils.nextElementWithin(in, outerTagDepth)) {
1178                 if (in.getAttributeValue(null, "name") != null) {
1179                     // Value elements.
1180                     String[] valueName = new String[1];
1181                     Object value = XmlUtil.readCurrentValue(in, valueName);
1182                     if (valueName[0] == null) {
1183                         throw new XmlPullParserException("Missing value name");
1184                     }
1185                     switch (valueName[0]) {
1186                         case XML_TAG_IDENTITY:
1187                             enterpriseConfig.setFieldValue(
1188                                     WifiEnterpriseConfig.IDENTITY_KEY, (String) value);
1189                             break;
1190                         case XML_TAG_ANON_IDENTITY:
1191                             enterpriseConfig.setFieldValue(
1192                                     WifiEnterpriseConfig.ANON_IDENTITY_KEY, (String) value);
1193                             break;
1194                         case XML_TAG_PASSWORD:
1195                             enterpriseConfig.setFieldValue(
1196                                     WifiEnterpriseConfig.PASSWORD_KEY, (String) value);
1197                             if (shouldExpectEncryptedCredentials
1198                                     && !TextUtils.isEmpty(enterpriseConfig.getFieldValue(
1199                                             WifiEnterpriseConfig.PASSWORD_KEY))) {
1200                                 // Indicates that encryption of password failed when it was last
1201                                 // written.
1202                                 Log.e(TAG, "password value not expected");
1203                             }
1204                             break;
1205                         case XML_TAG_CLIENT_CERT:
1206                             enterpriseConfig.setFieldValue(
1207                                     WifiEnterpriseConfig.CLIENT_CERT_KEY, (String) value);
1208                             break;
1209                         case XML_TAG_CA_CERT:
1210                             enterpriseConfig.setFieldValue(
1211                                     WifiEnterpriseConfig.CA_CERT_KEY, (String) value);
1212                             break;
1213                         case XML_TAG_SUBJECT_MATCH:
1214                             enterpriseConfig.setFieldValue(
1215                                     WifiEnterpriseConfig.SUBJECT_MATCH_KEY, (String) value);
1216                             break;
1217                         case XML_TAG_ENGINE:
1218                             enterpriseConfig.setFieldValue(
1219                                     WifiEnterpriseConfig.ENGINE_KEY, (String) value);
1220                             break;
1221                         case XML_TAG_ENGINE_ID:
1222                             enterpriseConfig.setFieldValue(
1223                                     WifiEnterpriseConfig.ENGINE_ID_KEY, (String) value);
1224                             break;
1225                         case XML_TAG_PRIVATE_KEY_ID:
1226                             enterpriseConfig.setFieldValue(
1227                                     WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, (String) value);
1228                             break;
1229                         case XML_TAG_ALT_SUBJECT_MATCH:
1230                             enterpriseConfig.setFieldValue(
1231                                     WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, (String) value);
1232                             break;
1233                         case XML_TAG_DOM_SUFFIX_MATCH:
1234                             enterpriseConfig.setFieldValue(
1235                                     WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, (String) value);
1236                             break;
1237                         case XML_TAG_CA_PATH:
1238                             enterpriseConfig.setFieldValue(
1239                                     WifiEnterpriseConfig.CA_PATH_KEY, (String) value);
1240                             break;
1241                         case XML_TAG_EAP_METHOD:
1242                             enterpriseConfig.setEapMethod((int) value);
1243                             break;
1244                         case XML_TAG_PHASE2_METHOD:
1245                             enterpriseConfig.setPhase2Method((int) value);
1246                             break;
1247                         case XML_TAG_PLMN:
1248                             enterpriseConfig.setPlmn((String) value);
1249                             break;
1250                         case XML_TAG_REALM:
1251                             enterpriseConfig.setRealm((String) value);
1252                             break;
1253                         default:
1254                             throw new XmlPullParserException(
1255                                   "Unknown value name found: " + valueName[0]);
1256                     }
1257                 } else {
1258                     String tagName = in.getName();
1259                     if (tagName == null) {
1260                         throw new XmlPullParserException("Unexpected null tag found");
1261                     }
1262                     switch (tagName) {
1263                         case XML_TAG_PASSWORD:
1264                             if (!shouldExpectEncryptedCredentials || encryptionUtil == null) {
1265                                 throw new XmlPullParserException(
1266                                         "encrypted password section not expected");
1267                             }
1268                             EncryptedData encryptedData =
1269                                     EncryptedDataXmlUtil.parseFromXml(in, outerTagDepth + 1);
1270                             byte[] passwordBytes = encryptionUtil.decrypt(encryptedData);
1271                             if (passwordBytes == null) {
1272                                 Log.wtf(TAG, "Decryption of password failed");
1273                             } else {
1274                                 enterpriseConfig.setFieldValue(
1275                                         WifiEnterpriseConfig.PASSWORD_KEY,
1276                                         new String(passwordBytes));
1277                             }
1278                             break;
1279                         default:
1280                             throw new XmlPullParserException(
1281                                   "Unknown tag name found: " + tagName);
1282                     }
1283                 }
1284             }
1285             return enterpriseConfig;
1286         }
1287     }
1288 
1289     /**
1290      * Utility class to serialize and deseriaize {@link EncryptedData} object to XML &
1291      * vice versa. This is used by {@link com.android.server.wifi.WifiConfigStore} module.
1292      */
1293     public static class EncryptedDataXmlUtil {
1294         /**
1295          * List of XML tags corresponding to EncryptedData object elements.
1296          */
1297         private static final String XML_TAG_ENCRYPTED_DATA = "EncryptedData";
1298         private static final String XML_TAG_IV = "IV";
1299 
1300         /**
1301          * Write the NetworkSelectionStatus data elements from the provided status to the XML
1302          * stream.
1303          *
1304          * @param out           XmlSerializer instance pointing to the XML stream.
1305          * @param encryptedData EncryptedData object to be serialized.
1306          */
writeToXml(XmlSerializer out, EncryptedData encryptedData)1307         public static void writeToXml(XmlSerializer out, EncryptedData encryptedData)
1308                 throws XmlPullParserException, IOException {
1309             XmlUtil.writeNextValue(
1310                     out, XML_TAG_ENCRYPTED_DATA, encryptedData.getEncryptedData());
1311             XmlUtil.writeNextValue(out, XML_TAG_IV, encryptedData.getIv());
1312         }
1313 
1314         /**
1315          * Parses the EncryptedData data elements from the provided XML stream to a
1316          * EncryptedData object.
1317          *
1318          * @param in            XmlPullParser instance pointing to the XML stream.
1319          * @param outerTagDepth depth of the outer tag in the XML document.
1320          * @return EncryptedData object if parsing is successful, null otherwise.
1321          */
parseFromXml(XmlPullParser in, int outerTagDepth)1322         public static EncryptedData parseFromXml(XmlPullParser in, int outerTagDepth)
1323                 throws XmlPullParserException, IOException {
1324             byte[] encryptedData = null;
1325             byte[] iv = null;
1326 
1327             // Loop through and parse out all the elements from the stream within this section.
1328             while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
1329                 String[] valueName = new String[1];
1330                 Object value = XmlUtil.readCurrentValue(in, valueName);
1331                 if (valueName[0] == null) {
1332                     throw new XmlPullParserException("Missing value name");
1333                 }
1334                 switch (valueName[0]) {
1335                     case XML_TAG_ENCRYPTED_DATA:
1336                         encryptedData = (byte[]) value;
1337                         break;
1338                     case XML_TAG_IV:
1339                         iv = (byte[]) value;
1340                         break;
1341                     default:
1342                         throw new XmlPullParserException(
1343                                 "Unknown value name found: " + valueName[0]);
1344                 }
1345             }
1346             return new EncryptedData(encryptedData, iv);
1347         }
1348     }
1349 }
1350 
1351