XmlUtil.java revision 06a2281303248446bacc87a00ab66ea1fdf0392d
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;
29
30import com.android.internal.util.XmlUtils;
31
32import org.xmlpull.v1.XmlPullParser;
33import org.xmlpull.v1.XmlPullParserException;
34import org.xmlpull.v1.XmlSerializer;
35
36import java.io.IOException;
37import java.net.Inet4Address;
38import java.net.InetAddress;
39import java.util.BitSet;
40
41/**
42 * Utils for manipulating XML data. This is essentially a wrapper over XmlUtils provided by core.
43 * The utility provides methods to write/parse section headers and write/parse values.
44 * This utility is designed for formatting the XML into the following format:
45 * <Document Header>
46 *  <Section 1 Header>
47 *   <Value 1>
48 *   <Value 2>
49 *   ...
50 *   <Sub Section 1 Header>
51 *    <Value 1>
52 *    <Value 2>
53 *    ...
54 *   </Sub Section 1 Header>
55 *  </Section 1 Header>
56 * </Document Header>
57 */
58public class XmlUtil {
59    private static final String TAG = "WifiXmlUtil";
60
61    /**
62     * Ensure that the XML stream is at a start tag or the end of document.
63     *
64     * @throws XmlPullParserException if parsing errors occur.
65     */
66    private static void gotoStartTag(XmlPullParser in)
67            throws XmlPullParserException, IOException {
68        int type = in.getEventType();
69        while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
70            type = in.next();
71        }
72    }
73
74    /**
75     * Ensure that the XML stream is at an end tag or the end of document.
76     *
77     * @throws XmlPullParserException if parsing errors occur.
78     */
79    private static void gotoEndTag(XmlPullParser in)
80            throws XmlPullParserException, IOException {
81        int type = in.getEventType();
82        while (type != XmlPullParser.END_TAG && type != XmlPullParser.END_DOCUMENT) {
83            type = in.next();
84        }
85    }
86
87    /**
88     * Start processing the XML stream at the document header.
89     *
90     * @param in         XmlPullParser instance pointing to the XML stream.
91     * @param headerName expected name for the start tag.
92     * @throws XmlPullParserException if parsing errors occur.
93     */
94    public static void gotoDocumentStart(XmlPullParser in, String headerName)
95            throws XmlPullParserException, IOException {
96        XmlUtils.beginDocument(in, headerName);
97    }
98
99    /**
100     * Move the XML stream to the next section header. The provided outerDepth is used to find
101     * sub sections within that depth.
102     *
103     * @param in         XmlPullParser instance pointing to the XML stream.
104     * @param headerName expected name for the start tag.
105     * @param outerDepth Find section within this depth.
106     * @return {@code true} if a start tag with the provided name is found, {@code false} otherwise
107     * @throws XmlPullParserException if parsing errors occur.
108     */
109    public static boolean gotoNextSection(XmlPullParser in, String headerName, int outerDepth)
110            throws XmlPullParserException, IOException {
111        while (XmlUtils.nextElementWithin(in, outerDepth)) {
112            if (in.getName().equals(headerName)) {
113                return true;
114            }
115        }
116        return false;
117    }
118
119    /**
120     * Checks if the stream is at the end of a section of values. This moves the stream to next tag
121     * and checks if it finds an end tag at the specified depth.
122     *
123     * @param in           XmlPullParser instance pointing to the XML stream.
124     * @param sectionDepth depth of the start tag of this section. Used to match the end tag.
125     * @return {@code true} if a end tag at the provided depth is found, {@code false} otherwise
126     * @throws XmlPullParserException if parsing errors occur.
127     */
128    public static boolean isNextSectionEnd(XmlPullParser in, int sectionDepth)
129            throws XmlPullParserException, IOException {
130        return (in.nextTag() == XmlPullParser.END_TAG && in.getDepth() == sectionDepth);
131    }
132
133    /**
134     * Read the current value in the XML stream using core XmlUtils and stores the retrieved
135     * value name in the string provided. This method reads the value contained in current start
136     * tag.
137     * Note: Because there could be genuine null values being read from the XML, this method raises
138     * an exception to indicate errors.
139     *
140     * @param in        XmlPullParser instance pointing to the XML stream.
141     * @param valueName An array of one string, used to return the name attribute
142     *                  of the value's tag.
143     * @return value retrieved from the XML stream.
144     * @throws XmlPullParserException if parsing errors occur.
145     */
146    public static Object readCurrentValue(XmlPullParser in, String[] valueName)
147            throws XmlPullParserException, IOException {
148        Object value = XmlUtils.readValueXml(in, valueName);
149        // XmlUtils.readValue does not always move the stream to the end of the tag. So, move
150        // it to the end tag before returning from here.
151        gotoEndTag(in);
152        return value;
153    }
154
155    /**
156     * Read the next value in the XML stream using core XmlUtils and ensure that it matches the
157     * provided name. This method moves the stream to the next start tag and reads the value
158     * contained in it.
159     * Note: Because there could be genuine null values being read from the XML, this method raises
160     * an exception to indicate errors.
161     *
162     * @param in XmlPullParser instance pointing to the XML stream.
163     * @return value retrieved from the XML stream.
164     * @throws XmlPullParserException if the value read does not match |expectedName|,
165     *                                or if parsing errors occur.
166     */
167    public static Object readNextValueWithName(XmlPullParser in, String expectedName)
168            throws XmlPullParserException, IOException {
169        String[] valueName = new String[1];
170        XmlUtils.nextElement(in);
171        Object value = readCurrentValue(in, valueName);
172        if (valueName[0].equals(expectedName)) {
173            return value;
174        }
175        throw new XmlPullParserException(
176                "Value not found. Expected: " + expectedName + ", but got: " + valueName[0]);
177    }
178
179    /**
180     * Write the XML document start with the provided document header name.
181     *
182     * @param out        XmlSerializer instance pointing to the XML stream.
183     * @param headerName name for the start tag.
184     */
185    public static void writeDocumentStart(XmlSerializer out, String headerName)
186            throws IOException {
187        out.startDocument(null, true);
188        out.startTag(null, headerName);
189    }
190
191    /**
192     * Write the XML document end with the provided document header name.
193     *
194     * @param out        XmlSerializer instance pointing to the XML stream.
195     * @param headerName name for the end tag.
196     */
197    public static void writeDocumentEnd(XmlSerializer out, String headerName)
198            throws IOException {
199        out.endTag(null, headerName);
200        out.endDocument();
201    }
202
203    /**
204     * Write a section start header tag with the provided section name.
205     *
206     * @param out        XmlSerializer instance pointing to the XML stream.
207     * @param headerName name for the start tag.
208     */
209    public static void writeNextSectionStart(XmlSerializer out, String headerName)
210            throws IOException {
211        out.startTag(null, headerName);
212    }
213
214    /**
215     * Write a section end header tag with the provided section name.
216     *
217     * @param out        XmlSerializer instance pointing to the XML stream.
218     * @param headerName name for the end tag.
219     */
220    public static void writeNextSectionEnd(XmlSerializer out, String headerName)
221            throws IOException {
222        out.endTag(null, headerName);
223    }
224
225    /**
226     * Write the value with the provided name in the XML stream using core XmlUtils.
227     *
228     * @param out   XmlSerializer instance pointing to the XML stream.
229     * @param name  name of the value.
230     * @param value value to be written.
231     */
232    public static void writeNextValue(XmlSerializer out, String name, Object value)
233            throws XmlPullParserException, IOException {
234        XmlUtils.writeValueXml(value, name, out);
235    }
236
237    /**
238     * Utility class to serialize and deseriaize WifConfiguration object to XML & vice versa.
239     * This is used by both #com.android.server.wifi.WifiConfigStore &
240     * #com.android.server.wifi.WifiBackupRestore modules.
241     * The |writeConfigurationToXml| has 2 versions, one for backup and one for config store.
242     * There is only 1 version of |parseXmlToConfiguration| for both backup & config store.
243     * The parse method is written so that any element added/deleted in future revisions can
244     * be easily handled.
245     */
246    public static class WifiConfigurationXmlUtil {
247        /**
248         * List of XML tags corresponding to WifiConfiguration object elements.
249         */
250        public static final String XML_TAG_SSID = "SSID";
251        public static final String XML_TAG_BSSID = "BSSID";
252        public static final String XML_TAG_CONFIG_KEY = "ConfigKey";
253        public static final String XML_TAG_PRE_SHARED_KEY = "PreSharedKey";
254        public static final String XML_TAG_WEP_KEYS = "WEPKeys";
255        public static final String XML_TAG_WEP_TX_KEY_INDEX = "WEPTxKeyIndex";
256        public static final String XML_TAG_HIDDEN_SSID = "HiddenSSID";
257        public static final String XML_TAG_ALLOWED_KEY_MGMT = "AllowedKeyMgmt";
258        public static final String XML_TAG_ALLOWED_PROTOCOLS = "AllowedProtocols";
259        public static final String XML_TAG_ALLOWED_AUTH_ALGOS = "AllowedAuthAlgos";
260        public static final String XML_TAG_SHARED = "Shared";
261        public static final String XML_TAG_CREATOR_UID = "CreatorUid";
262
263        /**
264         * Write WepKeys to the XML stream.
265         * WepKeys array is intialized in WifiConfiguration constructor, but all of the elements
266         * are set to null. User may chose to set any one of the key elements in WifiConfiguration.
267         * XmlUtils serialization doesn't handle this array of nulls well .
268         * So, write empty strings if some of the keys are not initialized and null if all of
269         * the elements are empty.
270         */
271        private static void writeWepKeysToXml(XmlSerializer out, String[] wepKeys)
272                throws XmlPullParserException, IOException {
273            String[] wepKeysToWrite = new String[wepKeys.length];
274            boolean hasWepKey = false;
275            for (int i = 0; i < wepKeys.length; i++) {
276                if (wepKeys[i] == null) {
277                    wepKeysToWrite[i] = new String();
278                } else {
279                    wepKeysToWrite[i] = wepKeys[i];
280                    hasWepKey = true;
281                }
282            }
283            if (hasWepKey) {
284                XmlUtil.writeNextValue(out, XML_TAG_WEP_KEYS, wepKeysToWrite);
285            } else {
286                XmlUtil.writeNextValue(out, XML_TAG_WEP_KEYS, null);
287            }
288        }
289
290        /**
291         * Write the Configuration data elements that are common for backup & config store to the
292         * XML stream.
293         *
294         * @param out           XmlSerializer instance pointing to the XML stream.
295         * @param configuration WifiConfiguration object to be serialized.
296         */
297        public static void writeCommonWifiConfigurationElementsToXml(XmlSerializer out,
298                WifiConfiguration configuration)
299                throws XmlPullParserException, IOException {
300            XmlUtil.writeNextValue(out, XML_TAG_CONFIG_KEY, configuration.configKey());
301            XmlUtil.writeNextValue(out, XML_TAG_SSID, configuration.SSID);
302            XmlUtil.writeNextValue(out, XML_TAG_BSSID, configuration.BSSID);
303            XmlUtil.writeNextValue(out, XML_TAG_PRE_SHARED_KEY, configuration.preSharedKey);
304            writeWepKeysToXml(out, configuration.wepKeys);
305            XmlUtil.writeNextValue(out, XML_TAG_WEP_TX_KEY_INDEX, configuration.wepTxKeyIndex);
306            XmlUtil.writeNextValue(out, XML_TAG_HIDDEN_SSID, configuration.hiddenSSID);
307            XmlUtil.writeNextValue(
308                    out, XML_TAG_ALLOWED_KEY_MGMT,
309                    configuration.allowedKeyManagement.toByteArray());
310            XmlUtil.writeNextValue(
311                    out, XML_TAG_ALLOWED_PROTOCOLS,
312                    configuration.allowedProtocols.toByteArray());
313            XmlUtil.writeNextValue(
314                    out, XML_TAG_ALLOWED_AUTH_ALGOS,
315                    configuration.allowedAuthAlgorithms.toByteArray());
316            XmlUtil.writeNextValue(out, XML_TAG_SHARED, configuration.shared);
317            XmlUtil.writeNextValue(out, XML_TAG_CREATOR_UID, configuration.creatorUid);
318        }
319
320        /**
321         * Write the Configuration data elements for backup from the provided Configuration to the
322         * XML stream.
323         * Note: This is a subset of the elements serialized for config store.
324         *
325         * @param out           XmlSerializer instance pointing to the XML stream.
326         * @param configuration WifiConfiguration object to be serialized.
327         */
328        public static void writeWifiConfigurationToXmlForBackup(XmlSerializer out,
329                WifiConfiguration configuration)
330                throws XmlPullParserException, IOException {
331            writeCommonWifiConfigurationElementsToXml(out, configuration);
332        }
333
334        /**
335         * Write the Configuration data elements for config store from the provided Configuration
336         * to the XML stream.
337         */
338        public static void writeWifiConfigurationToXmlForConfigStore(XmlSerializer out,
339                WifiConfiguration configuration)
340                throws XmlPullParserException, IOException {
341            writeCommonWifiConfigurationElementsToXml(out, configuration);
342            // TODO: Will need to add more elements which needs to be persisted.
343        }
344
345        /**
346         * Populate wepKeys array elements only if they were non-empty in the backup data.
347         * @throws XmlPullParserException if parsing errors occur.
348         */
349        private static void populateWepKeysFromXmlValue(Object value, String[] wepKeys)
350                throws XmlPullParserException, IOException {
351            String[] wepKeysInData = (String[]) value;
352            if (wepKeysInData == null) {
353                return;
354            }
355            if (wepKeysInData.length != wepKeys.length) {
356                throw new XmlPullParserException(
357                        "Invalid Wep Keys length: " + wepKeysInData.length);
358            }
359            for (int i = 0; i < wepKeys.length; i++) {
360                if (wepKeysInData[i].isEmpty()) {
361                    wepKeys[i] = null;
362                } else {
363                    wepKeys[i] = wepKeysInData[i];
364                }
365            }
366        }
367
368        /**
369         * Parses the configuration data elements from the provided XML stream to a Configuration.
370         * Note: This is used for parsing both backup data and config store data. Looping through
371         * the tags make it easy to add or remove elements in the future versions if needed.
372         *
373         * @param in            XmlPullParser instance pointing to the XML stream.
374         * @param outerTagDepth depth of the outer tag in the XML document.
375         * @return WifiConfiguration object if parsing is successful, null otherwise.
376         */
377        public static WifiConfiguration parseWifiConfigurationFromXml(XmlPullParser in,
378                int outerTagDepth)
379                throws XmlPullParserException, IOException {
380            WifiConfiguration configuration = new WifiConfiguration();
381            String configKeyInData = null;
382
383            // Loop through and parse out all the elements from the stream within this section.
384            while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
385                String[] valueName = new String[1];
386                Object value = XmlUtil.readCurrentValue(in, valueName);
387                if (valueName[0] == null) {
388                    Log.e(TAG, "Missing value name");
389                    return null;
390                }
391                switch (valueName[0]) {
392                    case XML_TAG_CONFIG_KEY:
393                        configKeyInData = (String) value;
394                        break;
395                    case XML_TAG_SSID:
396                        configuration.SSID = (String) value;
397                        break;
398                    case XML_TAG_BSSID:
399                        configuration.BSSID = (String) value;
400                        break;
401                    case XML_TAG_PRE_SHARED_KEY:
402                        configuration.preSharedKey = (String) value;
403                        break;
404                    case XML_TAG_WEP_KEYS:
405                        populateWepKeysFromXmlValue(value, configuration.wepKeys);
406                        break;
407                    case XML_TAG_WEP_TX_KEY_INDEX:
408                        configuration.wepTxKeyIndex = (int) value;
409                        break;
410                    case XML_TAG_HIDDEN_SSID:
411                        configuration.hiddenSSID = (boolean) value;
412                        break;
413                    case XML_TAG_ALLOWED_KEY_MGMT:
414                        byte[] allowedKeyMgmt = (byte[]) value;
415                        configuration.allowedKeyManagement = BitSet.valueOf(allowedKeyMgmt);
416                        break;
417                    case XML_TAG_ALLOWED_PROTOCOLS:
418                        byte[] allowedProtocols = (byte[]) value;
419                        configuration.allowedProtocols = BitSet.valueOf(allowedProtocols);
420                        break;
421                    case XML_TAG_ALLOWED_AUTH_ALGOS:
422                        byte[] allowedAuthAlgorithms = (byte[]) value;
423                        configuration.allowedAuthAlgorithms = BitSet.valueOf(allowedAuthAlgorithms);
424                        break;
425                    case XML_TAG_SHARED:
426                        configuration.shared = (boolean) value;
427                        break;
428                    case XML_TAG_CREATOR_UID:
429                        configuration.creatorUid = (int) value;
430                        break;
431                    default:
432                        Log.e(TAG, "Unknown value name found: " + valueName[0]);
433                        return null;
434                }
435            }
436            // We should now have all the data to calculate the configKey. Compare it against the
437            // configKey stored in the XML data.
438            String configKeyCalculated = configuration.configKey();
439            if (configKeyInData == null || !configKeyInData.equals(configKeyCalculated)) {
440                Log.e(TAG, "Configuration key does not match. Retrieved: " + configKeyInData
441                        + ", Calculated: " + configKeyCalculated);
442                return null;
443            }
444            return 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