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