XmlUtil.java revision 2fafcc56fda54b1adf8b6743beaac59dbb84dfec
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
17package com.android.server.wifi.util;
18
19import android.net.IpConfiguration;
20import android.net.IpConfiguration.IpAssignment;
21import android.net.IpConfiguration.ProxySettings;
22import android.net.LinkAddress;
23import android.net.NetworkUtils;
24import android.net.ProxyInfo;
25import android.net.RouteInfo;
26import android.net.StaticIpConfiguration;
27import android.net.wifi.WifiConfiguration;
28import android.util.Log;
29import android.util.Pair;
30
31import com.android.internal.util.XmlUtils;
32
33import org.xmlpull.v1.XmlPullParser;
34import org.xmlpull.v1.XmlPullParserException;
35import org.xmlpull.v1.XmlSerializer;
36
37import java.io.IOException;
38import java.net.Inet4Address;
39import java.net.InetAddress;
40import java.util.BitSet;
41
42/**
43 * Utils for manipulating XML data. This is essentially a wrapper over XmlUtils provided by core.
44 * The utility provides methods to write/parse section headers and write/parse values.
45 * This utility is designed for formatting the XML into the following format:
46 * <Document Header>
47 *  <Section 1 Header>
48 *   <Value 1>
49 *   <Value 2>
50 *   ...
51 *   <Sub Section 1 Header>
52 *    <Value 1>
53 *    <Value 2>
54 *    ...
55 *   </Sub Section 1 Header>
56 *  </Section 1 Header>
57 * </Document Header>
58 */
59public class XmlUtil {
60    private static final String TAG = "WifiXmlUtil";
61
62    /**
63     * Ensure that the XML stream is at a start tag or the end of document.
64     *
65     * @throws XmlPullParserException if parsing errors occur.
66     */
67    private static void gotoStartTag(XmlPullParser in)
68            throws XmlPullParserException, IOException {
69        int type = in.getEventType();
70        while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
71            type = in.next();
72        }
73    }
74
75    /**
76     * Ensure that the XML stream is at an end tag or the end of document.
77     *
78     * @throws XmlPullParserException if parsing errors occur.
79     */
80    private static void gotoEndTag(XmlPullParser in)
81            throws XmlPullParserException, IOException {
82        int type = in.getEventType();
83        while (type != XmlPullParser.END_TAG && type != XmlPullParser.END_DOCUMENT) {
84            type = in.next();
85        }
86    }
87
88    /**
89     * Start processing the XML stream at the document header.
90     *
91     * @param in         XmlPullParser instance pointing to the XML stream.
92     * @param headerName expected name for the start tag.
93     * @throws XmlPullParserException if parsing errors occur.
94     */
95    public static void gotoDocumentStart(XmlPullParser in, String headerName)
96            throws XmlPullParserException, IOException {
97        XmlUtils.beginDocument(in, headerName);
98    }
99
100    /**
101     * Move the XML stream to the next section header. The provided outerDepth is used to find
102     * sub sections within that depth.
103     *
104     * @param in         XmlPullParser instance pointing to the XML stream.
105     * @param headerName expected name for the start tag.
106     * @param outerDepth Find section within this depth.
107     * @return {@code true} if a start tag with the provided name is found, {@code false} otherwise
108     * @throws XmlPullParserException if parsing errors occur.
109     */
110    public static boolean gotoNextSection(XmlPullParser in, String headerName, int outerDepth)
111            throws XmlPullParserException, IOException {
112        while (XmlUtils.nextElementWithin(in, outerDepth)) {
113            if (in.getName().equals(headerName)) {
114                return true;
115            }
116        }
117        return false;
118    }
119
120    /**
121     * Checks if the stream is at the end of a section of values. This moves the stream to next tag
122     * and checks if it finds an end tag at the specified depth.
123     *
124     * @param in           XmlPullParser instance pointing to the XML stream.
125     * @param sectionDepth depth of the start tag of this section. Used to match the end tag.
126     * @return {@code true} if a end tag at the provided depth is found, {@code false} otherwise
127     * @throws XmlPullParserException if parsing errors occur.
128     */
129    public static boolean isNextSectionEnd(XmlPullParser in, int sectionDepth)
130            throws XmlPullParserException, IOException {
131        return (in.nextTag() == XmlPullParser.END_TAG && in.getDepth() == sectionDepth);
132    }
133
134    /**
135     * Read the current value in the XML stream using core XmlUtils and stores the retrieved
136     * value name in the string provided. This method reads the value contained in current start
137     * tag.
138     * Note: Because there could be genuine null values being read from the XML, this method raises
139     * an exception to indicate errors.
140     *
141     * @param in        XmlPullParser instance pointing to the XML stream.
142     * @param valueName An array of one string, used to return the name attribute
143     *                  of the value's tag.
144     * @return value retrieved from the XML stream.
145     * @throws XmlPullParserException if parsing errors occur.
146     */
147    public static Object readCurrentValue(XmlPullParser in, String[] valueName)
148            throws XmlPullParserException, IOException {
149        Object value = XmlUtils.readValueXml(in, valueName);
150        // XmlUtils.readValue does not always move the stream to the end of the tag. So, move
151        // it to the end tag before returning from here.
152        gotoEndTag(in);
153        return value;
154    }
155
156    /**
157     * Read the next value in the XML stream using core XmlUtils and ensure that it matches the
158     * provided name. This method moves the stream to the next start tag and reads the value
159     * contained in it.
160     * Note: Because there could be genuine null values being read from the XML, this method raises
161     * an exception to indicate errors.
162     *
163     * @param in XmlPullParser instance pointing to the XML stream.
164     * @return value retrieved from the XML stream.
165     * @throws XmlPullParserException if the value read does not match |expectedName|,
166     *                                or if parsing errors occur.
167     */
168    public static Object readNextValueWithName(XmlPullParser in, String expectedName)
169            throws XmlPullParserException, IOException {
170        String[] valueName = new String[1];
171        XmlUtils.nextElement(in);
172        Object value = readCurrentValue(in, valueName);
173        if (valueName[0].equals(expectedName)) {
174            return value;
175        }
176        throw new XmlPullParserException(
177                "Value not found. Expected: " + expectedName + ", but got: " + valueName[0]);
178    }
179
180    /**
181     * Write the XML document start with the provided document header name.
182     *
183     * @param out        XmlSerializer instance pointing to the XML stream.
184     * @param headerName name for the start tag.
185     */
186    public static void writeDocumentStart(XmlSerializer out, String headerName)
187            throws IOException {
188        out.startDocument(null, true);
189        out.startTag(null, headerName);
190    }
191
192    /**
193     * Write the XML document end with the provided document header name.
194     *
195     * @param out        XmlSerializer instance pointing to the XML stream.
196     * @param headerName name for the end tag.
197     */
198    public static void writeDocumentEnd(XmlSerializer out, String headerName)
199            throws IOException {
200        out.endTag(null, headerName);
201        out.endDocument();
202    }
203
204    /**
205     * Write a section start header tag with the provided section name.
206     *
207     * @param out        XmlSerializer instance pointing to the XML stream.
208     * @param headerName name for the start tag.
209     */
210    public static void writeNextSectionStart(XmlSerializer out, String headerName)
211            throws IOException {
212        out.startTag(null, headerName);
213    }
214
215    /**
216     * Write a section end header tag with the provided section name.
217     *
218     * @param out        XmlSerializer instance pointing to the XML stream.
219     * @param headerName name for the end tag.
220     */
221    public static void writeNextSectionEnd(XmlSerializer out, String headerName)
222            throws IOException {
223        out.endTag(null, headerName);
224    }
225
226    /**
227     * Write the value with the provided name in the XML stream using core XmlUtils.
228     *
229     * @param out   XmlSerializer instance pointing to the XML stream.
230     * @param name  name of the value.
231     * @param value value to be written.
232     */
233    public static void writeNextValue(XmlSerializer out, String name, Object value)
234            throws XmlPullParserException, IOException {
235        XmlUtils.writeValueXml(value, name, out);
236    }
237
238    /**
239     * Utility class to serialize and deseriaize WifConfiguration object to XML & vice versa.
240     * This is used by both #com.android.server.wifi.WifiConfigStore &
241     * #com.android.server.wifi.WifiBackupRestore modules.
242     * The |writeConfigurationToXml| has 2 versions, one for backup and one for config store.
243     * There is only 1 version of |parseXmlToConfiguration| for both backup & config store.
244     * The parse method is written so that any element added/deleted in future revisions can
245     * be easily handled.
246     */
247    public static class WifiConfigurationXmlUtil {
248        /**
249         * List of XML tags corresponding to WifiConfiguration object elements.
250         */
251        public static final String XML_TAG_SSID = "SSID";
252        public static final String XML_TAG_BSSID = "BSSID";
253        public static final String XML_TAG_CONFIG_KEY = "ConfigKey";
254        public static final String XML_TAG_PRE_SHARED_KEY = "PreSharedKey";
255        public static final String XML_TAG_WEP_KEYS = "WEPKeys";
256        public static final String XML_TAG_WEP_TX_KEY_INDEX = "WEPTxKeyIndex";
257        public static final String XML_TAG_HIDDEN_SSID = "HiddenSSID";
258        public static final String XML_TAG_ALLOWED_KEY_MGMT = "AllowedKeyMgmt";
259        public static final String XML_TAG_ALLOWED_PROTOCOLS = "AllowedProtocols";
260        public static final String XML_TAG_ALLOWED_AUTH_ALGOS = "AllowedAuthAlgos";
261        public static final String XML_TAG_SHARED = "Shared";
262        public static final String XML_TAG_CREATOR_UID = "CreatorUid";
263        public static final String XML_TAG_CREATOR_NAME = "CreatorName";
264
265        /**
266         * Write WepKeys to the XML stream.
267         * WepKeys array is intialized in WifiConfiguration constructor, but all of the elements
268         * are set to null. User may chose to set any one of the key elements in WifiConfiguration.
269         * XmlUtils serialization doesn't handle this array of nulls well .
270         * So, write empty strings if some of the keys are not initialized and null if all of
271         * the elements are empty.
272         */
273        private static void writeWepKeysToXml(XmlSerializer out, String[] wepKeys)
274                throws XmlPullParserException, IOException {
275            String[] wepKeysToWrite = new String[wepKeys.length];
276            boolean hasWepKey = false;
277            for (int i = 0; i < wepKeys.length; i++) {
278                if (wepKeys[i] == null) {
279                    wepKeysToWrite[i] = new String();
280                } else {
281                    wepKeysToWrite[i] = wepKeys[i];
282                    hasWepKey = true;
283                }
284            }
285            if (hasWepKey) {
286                XmlUtil.writeNextValue(out, XML_TAG_WEP_KEYS, wepKeysToWrite);
287            } else {
288                XmlUtil.writeNextValue(out, XML_TAG_WEP_KEYS, null);
289            }
290        }
291
292        /**
293         * Write the Configuration data elements that are common for backup & config store to the
294         * XML stream.
295         *
296         * @param out           XmlSerializer instance pointing to the XML stream.
297         * @param configuration WifiConfiguration object to be serialized.
298         */
299        public static void writeCommonWifiConfigurationElementsToXml(XmlSerializer out,
300                WifiConfiguration configuration)
301                throws XmlPullParserException, IOException {
302            XmlUtil.writeNextValue(out, XML_TAG_CONFIG_KEY, configuration.configKey());
303            XmlUtil.writeNextValue(out, XML_TAG_SSID, configuration.SSID);
304            XmlUtil.writeNextValue(out, XML_TAG_BSSID, configuration.BSSID);
305            XmlUtil.writeNextValue(out, XML_TAG_PRE_SHARED_KEY, configuration.preSharedKey);
306            writeWepKeysToXml(out, configuration.wepKeys);
307            XmlUtil.writeNextValue(out, XML_TAG_WEP_TX_KEY_INDEX, configuration.wepTxKeyIndex);
308            XmlUtil.writeNextValue(out, XML_TAG_HIDDEN_SSID, configuration.hiddenSSID);
309            XmlUtil.writeNextValue(
310                    out, XML_TAG_ALLOWED_KEY_MGMT,
311                    configuration.allowedKeyManagement.toByteArray());
312            XmlUtil.writeNextValue(
313                    out, XML_TAG_ALLOWED_PROTOCOLS,
314                    configuration.allowedProtocols.toByteArray());
315            XmlUtil.writeNextValue(
316                    out, XML_TAG_ALLOWED_AUTH_ALGOS,
317                    configuration.allowedAuthAlgorithms.toByteArray());
318            XmlUtil.writeNextValue(out, XML_TAG_SHARED, configuration.shared);
319        }
320
321        /**
322         * Write the Configuration data elements for backup from the provided Configuration to the
323         * XML stream.
324         * Note: This is a subset of the elements serialized for config store.
325         *
326         * @param out           XmlSerializer instance pointing to the XML stream.
327         * @param configuration WifiConfiguration object to be serialized.
328         */
329        public static void writeWifiConfigurationToXmlForBackup(XmlSerializer out,
330                WifiConfiguration configuration)
331                throws XmlPullParserException, IOException {
332            writeCommonWifiConfigurationElementsToXml(out, configuration);
333        }
334
335        /**
336         * Write the Configuration data elements for config store from the provided Configuration
337         * to the XML stream.
338         */
339        public static void writeWifiConfigurationToXmlForConfigStore(XmlSerializer out,
340                WifiConfiguration configuration)
341                throws XmlPullParserException, IOException {
342            writeCommonWifiConfigurationElementsToXml(out, configuration);
343            // TODO: Will need to add more elements which needs to be persisted.
344            XmlUtil.writeNextValue(out, XML_TAG_CREATOR_UID, configuration.creatorUid);
345            XmlUtil.writeNextValue(out, XML_TAG_CREATOR_NAME, configuration.creatorName);
346        }
347
348        /**
349         * Populate wepKeys array elements only if they were non-empty in the backup data.
350         *
351         * @throws XmlPullParserException if parsing errors occur.
352         */
353        private static void populateWepKeysFromXmlValue(Object value, String[] wepKeys)
354                throws XmlPullParserException, IOException {
355            String[] wepKeysInData = (String[]) value;
356            if (wepKeysInData == null) {
357                return;
358            }
359            if (wepKeysInData.length != wepKeys.length) {
360                throw new XmlPullParserException(
361                        "Invalid Wep Keys length: " + wepKeysInData.length);
362            }
363            for (int i = 0; i < wepKeys.length; i++) {
364                if (wepKeysInData[i].isEmpty()) {
365                    wepKeys[i] = null;
366                } else {
367                    wepKeys[i] = wepKeysInData[i];
368                }
369            }
370        }
371
372        /**
373         * Parses the configuration data elements from the provided XML stream to a Configuration.
374         * Note: This is used for parsing both backup data and config store data. Looping through
375         * the tags make it easy to add or remove elements in the future versions if needed.
376         *
377         * @param in            XmlPullParser instance pointing to the XML stream.
378         * @param outerTagDepth depth of the outer tag in the XML document.
379         * @return Pair<Config key, WifiConfiguration object> if parsing is successful,
380         * null otherwise.
381         */
382        public static Pair<String, WifiConfiguration> parseWifiConfigurationFromXml(
383                XmlPullParser in, int outerTagDepth)
384                throws XmlPullParserException, IOException {
385            WifiConfiguration configuration = new WifiConfiguration();
386            String configKeyInData = null;
387
388            // Loop through and parse out all the elements from the stream within this section.
389            while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
390                String[] valueName = new String[1];
391                Object value = XmlUtil.readCurrentValue(in, valueName);
392                if (valueName[0] == null) {
393                    Log.e(TAG, "Missing value name");
394                    return null;
395                }
396                switch (valueName[0]) {
397                    case XML_TAG_CONFIG_KEY:
398                        configKeyInData = (String) value;
399                        break;
400                    case XML_TAG_SSID:
401                        configuration.SSID = (String) value;
402                        break;
403                    case XML_TAG_BSSID:
404                        configuration.BSSID = (String) value;
405                        break;
406                    case XML_TAG_PRE_SHARED_KEY:
407                        configuration.preSharedKey = (String) value;
408                        break;
409                    case XML_TAG_WEP_KEYS:
410                        populateWepKeysFromXmlValue(value, configuration.wepKeys);
411                        break;
412                    case XML_TAG_WEP_TX_KEY_INDEX:
413                        configuration.wepTxKeyIndex = (int) value;
414                        break;
415                    case XML_TAG_HIDDEN_SSID:
416                        configuration.hiddenSSID = (boolean) value;
417                        break;
418                    case XML_TAG_ALLOWED_KEY_MGMT:
419                        byte[] allowedKeyMgmt = (byte[]) value;
420                        configuration.allowedKeyManagement = BitSet.valueOf(allowedKeyMgmt);
421                        break;
422                    case XML_TAG_ALLOWED_PROTOCOLS:
423                        byte[] allowedProtocols = (byte[]) value;
424                        configuration.allowedProtocols = BitSet.valueOf(allowedProtocols);
425                        break;
426                    case XML_TAG_ALLOWED_AUTH_ALGOS:
427                        byte[] allowedAuthAlgorithms = (byte[]) value;
428                        configuration.allowedAuthAlgorithms = BitSet.valueOf(allowedAuthAlgorithms);
429                        break;
430                    case XML_TAG_SHARED:
431                        configuration.shared = (boolean) value;
432                        break;
433                    case XML_TAG_CREATOR_UID:
434                        configuration.creatorUid = (int) value;
435                        break;
436                    case XML_TAG_CREATOR_NAME:
437                        configuration.creatorName = (String) value;
438                        break;
439                    default:
440                        Log.e(TAG, "Unknown value name found: " + valueName[0]);
441                        return null;
442                }
443            }
444            return Pair.create(configKeyInData, configuration);
445        }
446    }
447
448    /**
449     * Utility class to serialize and deseriaize IpConfiguration object to XML & vice versa.
450     * This is used by both #com.android.server.wifi.WifiConfigStore &
451     * #com.android.server.wifi.WifiBackupRestore modules.
452     */
453    public static class IpConfigurationXmlUtil {
454
455        /**
456         * List of XML tags corresponding to IpConfiguration object elements.
457         */
458        public static final String XML_TAG_IP_ASSIGNMENT = "IpAssignment";
459        public static final String XML_TAG_LINK_ADDRESS = "LinkAddress";
460        public static final String XML_TAG_LINK_PREFIX_LENGTH = "LinkPrefixLength";
461        public static final String XML_TAG_GATEWAY_ADDRESS = "GatewayAddress";
462        public static final String XML_TAG_DNS_SERVER_ADDRESSES = "DNSServers";
463        public static final String XML_TAG_PROXY_SETTINGS = "ProxySettings";
464        public static final String XML_TAG_PROXY_HOST = "ProxyHost";
465        public static final String XML_TAG_PROXY_PORT = "ProxyPort";
466        public static final String XML_TAG_PROXY_PAC_FILE = "ProxyPac";
467        public static final String XML_TAG_PROXY_EXCLUSION_LIST = "ProxyExclusionList";
468
469        /**
470         * Write the static IP configuration data elements to XML stream.
471         */
472        private static void writeStaticIpConfigurationToXml(XmlSerializer out,
473                StaticIpConfiguration staticIpConfiguration)
474                throws XmlPullParserException, IOException {
475            if (staticIpConfiguration.ipAddress != null) {
476                XmlUtil.writeNextValue(
477                        out, XML_TAG_LINK_ADDRESS,
478                        staticIpConfiguration.ipAddress.getAddress().getHostAddress());
479                XmlUtil.writeNextValue(
480                        out, XML_TAG_LINK_PREFIX_LENGTH,
481                        staticIpConfiguration.ipAddress.getPrefixLength());
482            } else {
483                XmlUtil.writeNextValue(
484                        out, XML_TAG_LINK_ADDRESS, null);
485                XmlUtil.writeNextValue(
486                        out, XML_TAG_LINK_PREFIX_LENGTH, null);
487            }
488            if (staticIpConfiguration.gateway != null) {
489                XmlUtil.writeNextValue(
490                        out, XML_TAG_GATEWAY_ADDRESS,
491                        staticIpConfiguration.gateway.getHostAddress());
492            } else {
493                XmlUtil.writeNextValue(
494                        out, XML_TAG_GATEWAY_ADDRESS, null);
495
496            }
497            if (staticIpConfiguration.dnsServers != null) {
498                // Create a string array of DNS server addresses
499                String[] dnsServers = new String[staticIpConfiguration.dnsServers.size()];
500                int dnsServerIdx = 0;
501                for (InetAddress inetAddr : staticIpConfiguration.dnsServers) {
502                    dnsServers[dnsServerIdx++] = inetAddr.getHostAddress();
503                }
504                XmlUtil.writeNextValue(
505                        out, XML_TAG_DNS_SERVER_ADDRESSES, dnsServers);
506            } else {
507                XmlUtil.writeNextValue(
508                        out, XML_TAG_DNS_SERVER_ADDRESSES, null);
509            }
510        }
511
512        /**
513         * Write the IP configuration data elements from the provided Configuration to the XML
514         * stream.
515         */
516        public static void writeIpConfigurationToXml(XmlSerializer out,
517                IpConfiguration ipConfiguration)
518                throws XmlPullParserException, IOException {
519            // Write IP assignment settings
520            XmlUtil.writeNextValue(out, XML_TAG_IP_ASSIGNMENT,
521                    ipConfiguration.ipAssignment.toString());
522            switch (ipConfiguration.ipAssignment) {
523                case STATIC:
524                    writeStaticIpConfigurationToXml(
525                            out, ipConfiguration.getStaticIpConfiguration());
526                    break;
527                default:
528                    break;
529            }
530
531            // Write proxy settings
532            XmlUtil.writeNextValue(
533                    out, XML_TAG_PROXY_SETTINGS,
534                    ipConfiguration.proxySettings.toString());
535            switch (ipConfiguration.proxySettings) {
536                case STATIC:
537                    XmlUtil.writeNextValue(
538                            out, XML_TAG_PROXY_HOST,
539                            ipConfiguration.httpProxy.getHost());
540                    XmlUtil.writeNextValue(
541                            out, XML_TAG_PROXY_PORT,
542                            ipConfiguration.httpProxy.getPort());
543                    XmlUtil.writeNextValue(
544                            out, XML_TAG_PROXY_EXCLUSION_LIST,
545                            ipConfiguration.httpProxy.getExclusionListAsString());
546                    break;
547                case PAC:
548                    XmlUtil.writeNextValue(
549                            out, XML_TAG_PROXY_PAC_FILE,
550                            ipConfiguration.httpProxy.getPacFileUrl().toString());
551                    break;
552                default:
553                    break;
554            }
555        }
556
557        /**
558         * Parse out the static IP configuration from the XML stream.
559         */
560        private static StaticIpConfiguration parseStaticIpConfigurationFromXml(XmlPullParser in)
561                throws XmlPullParserException, IOException {
562            StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
563
564            String linkAddressString =
565                    (String) XmlUtil.readNextValueWithName(in, XML_TAG_LINK_ADDRESS);
566            Integer linkPrefixLength =
567                    (Integer) XmlUtil.readNextValueWithName(in, XML_TAG_LINK_PREFIX_LENGTH);
568            if (linkAddressString != null && linkPrefixLength != null) {
569                LinkAddress linkAddress = new LinkAddress(
570                        NetworkUtils.numericToInetAddress(linkAddressString),
571                        linkPrefixLength);
572                if (linkAddress.getAddress() instanceof Inet4Address) {
573                    staticIpConfiguration.ipAddress = linkAddress;
574                } else {
575                    Log.w(TAG, "Non-IPv4 address: " + linkAddress);
576                }
577            }
578            String gatewayAddressString =
579                    (String) XmlUtil.readNextValueWithName(in, XML_TAG_GATEWAY_ADDRESS);
580            if (gatewayAddressString != null) {
581                LinkAddress dest = null;
582                InetAddress gateway =
583                        NetworkUtils.numericToInetAddress(gatewayAddressString);
584                RouteInfo route = new RouteInfo(dest, gateway);
585                if (route.isIPv4Default()) {
586                    staticIpConfiguration.gateway = gateway;
587                } else {
588                    Log.w(TAG, "Non-IPv4 default route: " + route);
589                }
590            }
591            String[] dnsServerAddressesString =
592                    (String[]) XmlUtil.readNextValueWithName(in, XML_TAG_DNS_SERVER_ADDRESSES);
593            if (dnsServerAddressesString != null) {
594                for (String dnsServerAddressString : dnsServerAddressesString) {
595                    InetAddress dnsServerAddress =
596                            NetworkUtils.numericToInetAddress(dnsServerAddressString);
597                    staticIpConfiguration.dnsServers.add(dnsServerAddress);
598                }
599            }
600            return staticIpConfiguration;
601        }
602
603        /**
604         * Parses the IP configuration data elements from the provided XML stream to a
605         * IpConfiguration.
606         *
607         * @param in            XmlPullParser instance pointing to the XML stream.
608         * @param outerTagDepth depth of the outer tag in the XML document.
609         * @return IpConfiguration object if parsing is successful, null otherwise.
610         */
611        public static IpConfiguration parseIpConfigurationFromXml(XmlPullParser in,
612                int outerTagDepth)
613                throws XmlPullParserException, IOException {
614            IpConfiguration ipConfiguration = new IpConfiguration();
615
616            // Parse out the IP assignment info first.
617            String ipAssignmentString =
618                    (String) XmlUtil.readNextValueWithName(in, XML_TAG_IP_ASSIGNMENT);
619            IpAssignment ipAssignment = IpAssignment.valueOf(ipAssignmentString);
620            ipConfiguration.setIpAssignment(ipAssignment);
621            switch (ipAssignment) {
622                case STATIC:
623                    ipConfiguration.setStaticIpConfiguration(parseStaticIpConfigurationFromXml(in));
624                    break;
625                case DHCP:
626                case UNASSIGNED:
627                    break;
628                default:
629                    Log.wtf(TAG, "Unknown ip assignment type: " + ipAssignment);
630                    return null;
631            }
632
633            // Parse out the proxy settings next.
634            String proxySettingsString =
635                    (String) XmlUtil.readNextValueWithName(in, XML_TAG_PROXY_SETTINGS);
636            ProxySettings proxySettings = ProxySettings.valueOf(proxySettingsString);
637            ipConfiguration.setProxySettings(proxySettings);
638            switch (proxySettings) {
639                case STATIC:
640                    String proxyHost =
641                            (String) XmlUtil.readNextValueWithName(in, XML_TAG_PROXY_HOST);
642                    int proxyPort =
643                            (int) XmlUtil.readNextValueWithName(in, XML_TAG_PROXY_PORT);
644                    String proxyExclusionList =
645                            (String) XmlUtil.readNextValueWithName(
646                                    in, XML_TAG_PROXY_EXCLUSION_LIST);
647                    ipConfiguration.setHttpProxy(
648                            new ProxyInfo(proxyHost, proxyPort, proxyExclusionList));
649                    break;
650                case PAC:
651                    String proxyPacFile =
652                            (String) XmlUtil.readNextValueWithName(in, XML_TAG_PROXY_PAC_FILE);
653                    ipConfiguration.setHttpProxy(new ProxyInfo(proxyPacFile));
654                    break;
655                case NONE:
656                case UNASSIGNED:
657                    break;
658                default:
659                    Log.wtf(TAG, "Unknown proxy settings type: " + proxySettings);
660                    return null;
661            }
662            return ipConfiguration;
663        }
664    }
665}
666
667