1/*
2 * Copyright (C) 2017 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.hotspot2;
18
19import android.net.wifi.hotspot2.PasspointConfiguration;
20import android.net.wifi.hotspot2.pps.Credential;
21import android.net.wifi.hotspot2.pps.HomeSp;
22import android.net.wifi.hotspot2.pps.Policy;
23import android.net.wifi.hotspot2.pps.UpdateParameter;
24
25import com.android.internal.util.XmlUtils;
26import com.android.server.wifi.util.XmlUtil;
27
28import org.xmlpull.v1.XmlPullParser;
29import org.xmlpull.v1.XmlPullParserException;
30import org.xmlpull.v1.XmlSerializer;
31
32import java.io.IOException;
33import java.util.ArrayList;
34import java.util.HashMap;
35import java.util.List;
36import java.util.Map;
37
38/**
39 * Utility class for serialize and deserialize Passpoint related configurations to/from XML string.
40 */
41public class PasspointXmlUtils {
42    // XML section header tags.
43    private static final String XML_TAG_SECTION_HEADER_HOMESP = "HomeSP";
44    private static final String XML_TAG_SECTION_HEADER_CREDENTIAL = "Credential";
45    private static final String XML_TAG_SECTION_HEADER_USER_CREDENTIAL = "UserCredential";
46    private static final String XML_TAG_SECTION_HEADER_CERT_CREDENTIAL = "CertCredential";
47    private static final String XML_TAG_SECTION_HEADER_SIM_CREDENTIAL = "SimCredential";
48    private static final String XML_TAG_SECTION_HEADER_POLICY = "Policy";
49    private static final String XML_TAG_SECTION_HEADER_PREFERRED_ROAMING_PARTNER_LIST =
50            "RoamingPartnerList";
51    private static final String XML_TAG_SECTION_HEADER_ROAMING_PARTNER = "RoamingPartner";
52    private static final String XML_TAG_SECTION_HEADER_POLICY_UPDATE = "PolicyUpdate";
53    private static final String XML_TAG_SECTION_HEADER_SUBSCRIPTION_UPDATE = "SubscriptionUpdate";
54    private static final String XML_TAG_SECTION_HEADER_REQUIRED_PROTO_PORT_MAP =
55            "RequiredProtoPortMap";
56    private static final String XML_TAG_SECTION_HEADER_PROTO_PORT = "ProtoPort";
57
58    // XML value tags.
59    private static final String XML_TAG_FQDN = "FQDN";
60    private static final String XML_TAG_FRIENDLY_NAME = "FriendlyName";
61    private static final String XML_TAG_ICON_URL = "IconURL";
62    private static final String XML_TAG_HOME_NETWORK_IDS = "HomeNetworkIDs";
63    private static final String XML_TAG_MATCH_ALL_OIS = "MatchAllOIs";
64    private static final String XML_TAG_MATCH_ANY_OIS = "MatchAnyOIs";
65    private static final String XML_TAG_OTHER_HOME_PARTNERS = "OtherHomePartners";
66    private static final String XML_TAG_ROAMING_CONSORTIUM_OIS = "RoamingConsortiumOIs";
67    private static final String XML_TAG_CREATION_TIME = "CreationTime";
68    private static final String XML_TAG_EXPIRATION_TIME = "ExpirationTime";
69    private static final String XML_TAG_REALM = "Realm";
70    private static final String XML_TAG_CHECK_AAA_SERVER_CERT_STATUS = "CheckAAAServerCertStatus";
71    private static final String XML_TAG_USERNAME = "Username";
72    private static final String XML_TAG_PASSWORD = "Password";
73    private static final String XML_TAG_MACHINE_MANAGED = "MachineManaged";
74    private static final String XML_TAG_SOFT_TOKEN_APP = "SoftTokenApp";
75    private static final String XML_TAG_ABLE_TO_SHARE = "AbleToShare";
76    private static final String XML_TAG_EAP_TYPE = "EAPType";
77    private static final String XML_TAG_NON_EAP_INNER_METHOD = "NonEAPInnerMethod";
78    private static final String XML_TAG_CERT_TYPE = "CertType";
79    private static final String XML_TAG_CERT_SHA256_FINGERPRINT = "CertSHA256Fingerprint";
80    private static final String XML_TAG_IMSI = "IMSI";
81    private static final String XML_TAG_MIN_HOME_DOWNLINK_BANDWIDTH = "MinHomeDownlinkBandwidth";
82    private static final String XML_TAG_MIN_HOME_UPLINK_BANDWIDTH = "MinHomeUplinkBandwidth";
83    private static final String XML_TAG_MIN_ROAMING_DOWNLINK_BANDWIDTH =
84            "MinRoamingDownlinkBandwidth";
85    private static final String XML_TAG_MIN_ROAMING_UPLINK_BANDWIDTH =
86            "MinRoamingUplinkBandwidth";
87    private static final String XML_TAG_EXCLUDED_SSID_LIST = "ExcludedSSIDList";
88    private static final String XML_TAG_PROTO = "Proto";
89    private static final String XML_TAG_PORTS = "Ports";
90    private static final String XML_TAG_MAXIMUM_BSS_LOAD_VALUE = "MaximumBSSLoadValue";
91    private static final String XML_TAG_FQDN_EXACT_MATCH = "FQDNExactMatch";
92    private static final String XML_TAG_PRIORITY = "Priority";
93    private static final String XML_TAG_COUNTRIES = "Countries";
94    private static final String XML_TAG_UPDATE_INTERVAL = "UpdateInterval";
95    private static final String XML_TAG_UPDATE_METHOD = "UpdateMethod";
96    private static final String XML_TAG_RESTRICTION = "Restriction";
97    private static final String XML_TAG_SERVER_URI = "ServerURI";
98    private static final String XML_TAG_TRUST_ROOT_CERT_URL = "TrustRootCertURL";
99    private static final String XML_TAG_TRUST_ROOT_CERT_SHA256_FINGERPRINT =
100            "TrustRootCertSHA256Fingerprint";
101    private static final String XML_TAG_TRUST_ROOT_CERT_LIST = "TrustRootCertList";
102    private static final String XML_TAG_UPDATE_IDENTIFIER = "UpdateIdentifier";
103    private static final String XML_TAG_CREDENTIAL_PRIORITY = "CredentialPriority";
104    private static final String XML_TAG_SUBSCRIPTION_CREATION_TIME = "SubscriptionCreationTime";
105    private static final String XML_TAG_SUBSCRIPTION_EXPIRATION_TIME =
106            "SubscriptionExpirationTime";
107    private static final String XML_TAG_SUBSCRIPTION_TYPE = "SubscriptionType";
108    private static final String XML_TAG_USAGE_LIMIT_TIME_PERIOD = "UsageLimitTimePeriod";
109    private static final String XML_TAG_USAGE_LIMIT_START_TIME = "UsageLimitStartTime";
110    private static final String XML_TAG_USAGE_LIMIT_DATA_LIMIT = "UsageLimitDataLimit";
111    private static final String XML_TAG_USAGE_LIMIT_TIME_LIMIT = "UsageLimitTimeLimit";
112
113    /**
114     * Serialize a {@link PasspointConfiguration} to the output stream as a XML block.
115     *
116     * @param out The output stream to serialize to
117     * @param config The configuration to serialize
118     * @throws XmlPullParserException
119     * @throws IOException
120     */
121    public static void serializePasspointConfiguration(XmlSerializer out,
122            PasspointConfiguration config) throws XmlPullParserException, IOException {
123        XmlUtil.writeNextValue(out, XML_TAG_UPDATE_IDENTIFIER, config.getUpdateIdentifier());
124        XmlUtil.writeNextValue(out, XML_TAG_CREDENTIAL_PRIORITY, config.getCredentialPriority());
125        XmlUtil.writeNextValue(out, XML_TAG_TRUST_ROOT_CERT_LIST, config.getTrustRootCertList());
126        XmlUtil.writeNextValue(out, XML_TAG_SUBSCRIPTION_CREATION_TIME,
127                config.getSubscriptionCreationTimeInMillis());
128        XmlUtil.writeNextValue(out, XML_TAG_SUBSCRIPTION_EXPIRATION_TIME,
129                config.getSubscriptionExpirationTimeInMillis());
130        XmlUtil.writeNextValue(out, XML_TAG_SUBSCRIPTION_TYPE, config.getSubscriptionType());
131        XmlUtil.writeNextValue(out, XML_TAG_USAGE_LIMIT_TIME_PERIOD,
132                config.getUsageLimitUsageTimePeriodInMinutes());
133        XmlUtil.writeNextValue(out, XML_TAG_USAGE_LIMIT_START_TIME,
134                config.getUsageLimitStartTimeInMillis());
135        XmlUtil.writeNextValue(out, XML_TAG_USAGE_LIMIT_DATA_LIMIT,
136                config.getUsageLimitDataLimit());
137        XmlUtil.writeNextValue(out, XML_TAG_USAGE_LIMIT_TIME_LIMIT,
138                config.getUsageLimitTimeLimitInMinutes());
139        serializeHomeSp(out, config.getHomeSp());
140        serializeCredential(out, config.getCredential());
141        serializePolicy(out, config.getPolicy());
142        serializeUpdateParameter(out, XML_TAG_SECTION_HEADER_SUBSCRIPTION_UPDATE,
143                config.getSubscriptionUpdate());
144    }
145
146    /**
147     * Deserialize a {@link PasspointConfiguration} from an input stream containing XML block.
148     *
149     * @param in The input stream to read from
150     * @param outerTagDepth The tag depth of the current XML section
151     * @return {@link PasspointConfiguration}
152     * @throws XmlPullParserException
153     * @throws IOException
154     */
155    public static PasspointConfiguration deserializePasspointConfiguration(XmlPullParser in,
156            int outerTagDepth) throws XmlPullParserException, IOException {
157        PasspointConfiguration config = new PasspointConfiguration();
158        while (XmlUtils.nextElementWithin(in, outerTagDepth)) {
159            if (isValueElement(in)) {
160                // Value elements.
161                String[] name = new String[1];
162                Object value = XmlUtil.readCurrentValue(in, name);
163                switch (name[0]) {
164                    case XML_TAG_UPDATE_IDENTIFIER:
165                        config.setUpdateIdentifier((int) value);
166                        break;
167                    case XML_TAG_CREDENTIAL_PRIORITY:
168                        config.setCredentialPriority((int) value);
169                        break;
170                    case XML_TAG_TRUST_ROOT_CERT_LIST:
171                        config.setTrustRootCertList((Map<String, byte[]>) value);
172                        break;
173                    case XML_TAG_SUBSCRIPTION_CREATION_TIME:
174                        config.setSubscriptionCreationTimeInMillis((long) value);
175                        break;
176                    case XML_TAG_SUBSCRIPTION_EXPIRATION_TIME:
177                        config.setSubscriptionExpirationTimeInMillis((long) value);
178                        break;
179                    case XML_TAG_SUBSCRIPTION_TYPE:
180                        config.setSubscriptionType((String) value);
181                        break;
182                    case XML_TAG_USAGE_LIMIT_TIME_PERIOD:
183                        config.setUsageLimitUsageTimePeriodInMinutes((long) value);
184                        break;
185                    case XML_TAG_USAGE_LIMIT_START_TIME:
186                        config.setUsageLimitStartTimeInMillis((long) value);
187                        break;
188                    case XML_TAG_USAGE_LIMIT_DATA_LIMIT:
189                        config.setUsageLimitDataLimit((long) value);
190                        break;
191                    case XML_TAG_USAGE_LIMIT_TIME_LIMIT:
192                        config.setUsageLimitTimeLimitInMinutes((long) value);
193                        break;
194                    default:
195                        throw new XmlPullParserException("Unknown value under "
196                                + "PasspointConfiguration: " + in.getName());
197                }
198            } else {
199                // Section elements.
200                switch (in.getName()) {
201                    case XML_TAG_SECTION_HEADER_HOMESP:
202                        config.setHomeSp(deserializeHomeSP(in, outerTagDepth + 1));
203                        break;
204                    case XML_TAG_SECTION_HEADER_CREDENTIAL:
205                        config.setCredential(deserializeCredential(in, outerTagDepth + 1));
206                        break;
207                    case XML_TAG_SECTION_HEADER_POLICY:
208                        config.setPolicy(deserializePolicy(in, outerTagDepth + 1));
209                        break;
210                    case XML_TAG_SECTION_HEADER_SUBSCRIPTION_UPDATE:
211                        config.setSubscriptionUpdate(
212                                deserializeUpdateParameter(in, outerTagDepth + 1));
213                        break;
214                    default:
215                        throw new XmlPullParserException("Unknown section under "
216                                + "PasspointConfiguration: " + in.getName());
217                }
218            }
219        }
220        return config;
221    }
222
223    /**
224     * Serialize a {@link HomeSp} to an output stream as a XML block.
225     *
226     * @param out The output stream to serialize data to
227     * @param homeSp The {@link HomeSp} to serialize
228     * @throws XmlPullParserException
229     * @throws IOException
230     */
231    private static void serializeHomeSp(XmlSerializer out, HomeSp homeSp)
232            throws XmlPullParserException, IOException {
233        if (homeSp == null) {
234            return;
235        }
236        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_HOMESP);
237        XmlUtil.writeNextValue(out, XML_TAG_FQDN, homeSp.getFqdn());
238        XmlUtil.writeNextValue(out, XML_TAG_FRIENDLY_NAME, homeSp.getFriendlyName());
239        XmlUtil.writeNextValue(out, XML_TAG_ICON_URL, homeSp.getIconUrl());
240        XmlUtil.writeNextValue(out, XML_TAG_HOME_NETWORK_IDS, homeSp.getHomeNetworkIds());
241        XmlUtil.writeNextValue(out, XML_TAG_MATCH_ALL_OIS, homeSp.getMatchAllOis());
242        XmlUtil.writeNextValue(out, XML_TAG_MATCH_ANY_OIS, homeSp.getMatchAnyOis());
243        XmlUtil.writeNextValue(out, XML_TAG_OTHER_HOME_PARTNERS, homeSp.getOtherHomePartners());
244        XmlUtil.writeNextValue(out, XML_TAG_ROAMING_CONSORTIUM_OIS,
245                homeSp.getRoamingConsortiumOis());
246        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_HOMESP);
247    }
248
249    /**
250     * Serialize a {@link Credential} to an output stream as a XML block.
251     *
252     * @param out The output stream to serialize to
253     * @param credential The {@link Credential} to serialize
254     * @throws XmlPullParserException
255     * @throws IOException
256     */
257    private static void serializeCredential(XmlSerializer out, Credential credential)
258            throws XmlPullParserException, IOException {
259        if (credential == null) {
260            return;
261        }
262        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_CREDENTIAL);
263        XmlUtil.writeNextValue(out, XML_TAG_CREATION_TIME, credential.getCreationTimeInMillis());
264        XmlUtil.writeNextValue(out, XML_TAG_EXPIRATION_TIME,
265                credential.getExpirationTimeInMillis());
266        XmlUtil.writeNextValue(out, XML_TAG_REALM, credential.getRealm());
267        XmlUtil.writeNextValue(out, XML_TAG_CHECK_AAA_SERVER_CERT_STATUS,
268                credential.getCheckAaaServerCertStatus());
269        serializeUserCredential(out, credential.getUserCredential());
270        serializeCertCredential(out, credential.getCertCredential());
271        serializeSimCredential(out, credential.getSimCredential());
272        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_CREDENTIAL);
273    }
274
275    /**
276     * Serialize a {@link Policy} to an output stream as a XML block.
277     *
278     * @param out The output stream to serialize to
279     * @param policy The {@link Policy} to serialize
280     * @throws XmlPullParserException
281     * @throws IOException
282     */
283    private static void serializePolicy(XmlSerializer out, Policy policy)
284            throws XmlPullParserException, IOException {
285        if (policy == null) {
286            return;
287        }
288        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_POLICY);
289        XmlUtil.writeNextValue(out, XML_TAG_MIN_HOME_DOWNLINK_BANDWIDTH,
290                policy.getMinHomeDownlinkBandwidth());
291        XmlUtil.writeNextValue(out, XML_TAG_MIN_HOME_UPLINK_BANDWIDTH,
292                policy.getMinHomeUplinkBandwidth());
293        XmlUtil.writeNextValue(out, XML_TAG_MIN_ROAMING_DOWNLINK_BANDWIDTH,
294                policy.getMinRoamingDownlinkBandwidth());
295        XmlUtil.writeNextValue(out, XML_TAG_MIN_ROAMING_UPLINK_BANDWIDTH,
296                policy.getMinRoamingUplinkBandwidth());
297        XmlUtil.writeNextValue(out, XML_TAG_EXCLUDED_SSID_LIST, policy.getExcludedSsidList());
298        XmlUtil.writeNextValue(out, XML_TAG_MAXIMUM_BSS_LOAD_VALUE,
299                policy.getMaximumBssLoadValue());
300        serializeProtoPortMap(out, policy.getRequiredProtoPortMap());
301        serializeUpdateParameter(out, XML_TAG_SECTION_HEADER_POLICY_UPDATE,
302                policy.getPolicyUpdate());
303        serializePreferredRoamingPartnerList(out, policy.getPreferredRoamingPartnerList());
304        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_POLICY);
305    }
306
307    /**
308     * Serialize a {@link android.net.wifi.hotspot2.pps.Credential.UserCredential} to an output
309     * stream as a XML block.
310     *
311     * @param out The output stream to serialize data to
312     * @param userCredential The UserCredential to serialize
313     * @throws XmlPullParserException
314     * @throws IOException
315     */
316    private static void serializeUserCredential(XmlSerializer out,
317            Credential.UserCredential userCredential) throws XmlPullParserException, IOException {
318        if (userCredential == null) {
319            return;
320        }
321        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_USER_CREDENTIAL);
322        XmlUtil.writeNextValue(out, XML_TAG_USERNAME, userCredential.getUsername());
323        XmlUtil.writeNextValue(out, XML_TAG_PASSWORD, userCredential.getPassword());
324        XmlUtil.writeNextValue(out, XML_TAG_MACHINE_MANAGED, userCredential.getMachineManaged());
325        XmlUtil.writeNextValue(out, XML_TAG_SOFT_TOKEN_APP, userCredential.getSoftTokenApp());
326        XmlUtil.writeNextValue(out, XML_TAG_ABLE_TO_SHARE, userCredential.getAbleToShare());
327        XmlUtil.writeNextValue(out, XML_TAG_EAP_TYPE, userCredential.getEapType());
328        XmlUtil.writeNextValue(out, XML_TAG_NON_EAP_INNER_METHOD,
329                userCredential.getNonEapInnerMethod());
330        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_USER_CREDENTIAL);
331    }
332
333    /**
334     * Serialize a {@link android.net.wifi.hotspot2.pps.Credential.CertificateCredential} to an
335     * output stream as a XML block.
336     *
337     * @param out The output stream to serialize data to
338     * @param certCredential The CertificateCredential to serialize
339     * @throws XmlPullParserException
340     * @throws IOException
341     */
342    private static void serializeCertCredential(XmlSerializer out,
343            Credential.CertificateCredential certCredential)
344                    throws XmlPullParserException, IOException {
345        if (certCredential == null) {
346            return;
347        }
348        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_CERT_CREDENTIAL);
349        XmlUtil.writeNextValue(out, XML_TAG_CERT_TYPE, certCredential.getCertType());
350        XmlUtil.writeNextValue(out, XML_TAG_CERT_SHA256_FINGERPRINT,
351                certCredential.getCertSha256Fingerprint());
352        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_CERT_CREDENTIAL);
353    }
354
355    /**
356     * Serialize a {@link android.net.wifi.hotspot2.pps.Credential.SimCredential} to an
357     * output stream as a XML block.
358     *
359     * @param out The output stream to serialize data to
360     * @param simCredential The SimCredential to serialize
361     * @throws XmlPullParserException
362     * @throws IOException
363     */
364    private static void serializeSimCredential(XmlSerializer out,
365            Credential.SimCredential simCredential) throws XmlPullParserException, IOException {
366        if (simCredential == null) {
367            return;
368        }
369        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_SIM_CREDENTIAL);
370        XmlUtil.writeNextValue(out, XML_TAG_IMSI, simCredential.getImsi());
371        XmlUtil.writeNextValue(out, XML_TAG_EAP_TYPE, simCredential.getEapType());
372        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_SIM_CREDENTIAL);
373    }
374
375    /**
376     * Serialize a preferred roaming partner list to an output stream as a XML block.
377     *
378     * @param out The output stream to serialize data to
379     * @param preferredRoamingPartnerList The partner list to serialize
380     * @throws XmlPullParserException
381     * @throws IOException
382     */
383    private static void serializePreferredRoamingPartnerList(XmlSerializer out,
384            List<Policy.RoamingPartner> preferredRoamingPartnerList)
385                    throws XmlPullParserException, IOException {
386        if (preferredRoamingPartnerList == null) {
387            return;
388        }
389        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PREFERRED_ROAMING_PARTNER_LIST);
390        for (Policy.RoamingPartner partner : preferredRoamingPartnerList) {
391            XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_ROAMING_PARTNER);
392            XmlUtil.writeNextValue(out, XML_TAG_FQDN, partner.getFqdn());
393            XmlUtil.writeNextValue(out, XML_TAG_FQDN_EXACT_MATCH, partner.getFqdnExactMatch());
394            XmlUtil.writeNextValue(out, XML_TAG_PRIORITY, partner.getPriority());
395            XmlUtil.writeNextValue(out, XML_TAG_COUNTRIES, partner.getCountries());
396            XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_ROAMING_PARTNER);
397        }
398        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PREFERRED_ROAMING_PARTNER_LIST);
399    }
400
401    /**
402     * Serialize a {@link UpdateParameter} to an output stream as a XML block.  The
403     * {@link UpdateParameter} are used for describing Subscription Update and Policy Update.
404     *
405     * @param out The output stream to serialize data to
406     * @param type The type the {@link UpdateParameter} is used for
407     * @param param The {@link UpdateParameter} to serialize
408     * @throws XmlPullParserException
409     * @throws IOException
410     */
411    private static void serializeUpdateParameter(XmlSerializer out, String type,
412            UpdateParameter param) throws XmlPullParserException, IOException {
413        if (param == null) {
414            return;
415        }
416        XmlUtil.writeNextSectionStart(out, type);
417        XmlUtil.writeNextValue(out, XML_TAG_UPDATE_INTERVAL, param.getUpdateIntervalInMinutes());
418        XmlUtil.writeNextValue(out, XML_TAG_UPDATE_METHOD, param.getUpdateMethod());
419        XmlUtil.writeNextValue(out, XML_TAG_RESTRICTION, param.getRestriction());
420        XmlUtil.writeNextValue(out, XML_TAG_SERVER_URI, param.getServerUri());
421        XmlUtil.writeNextValue(out, XML_TAG_USERNAME, param.getUsername());
422        XmlUtil.writeNextValue(out, XML_TAG_PASSWORD, param.getBase64EncodedPassword());
423        XmlUtil.writeNextValue(out, XML_TAG_TRUST_ROOT_CERT_URL, param.getTrustRootCertUrl());
424        XmlUtil.writeNextValue(out, XML_TAG_TRUST_ROOT_CERT_SHA256_FINGERPRINT,
425                param.getTrustRootCertSha256Fingerprint());
426        XmlUtil.writeNextSectionEnd(out, type);
427    }
428
429    /**
430     * Serialize a Protocol-to-Ports map to an output stream as a XML block.  We're not able
431     * to use {@link XmlUtil#writeNextValue} to write this map, since that function only works for
432     * maps with String key.
433     *
434     * @param out The output stream to serialize data to
435     * @param protoPortMap The proto port map to serialize
436     * @throws XmlPullParserException
437     * @throws IOException
438     */
439    private static void serializeProtoPortMap(XmlSerializer out, Map<Integer, String> protoPortMap)
440            throws XmlPullParserException, IOException {
441        if (protoPortMap == null) {
442            return;
443        }
444        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_REQUIRED_PROTO_PORT_MAP);
445        for (Map.Entry<Integer, String> entry : protoPortMap.entrySet()) {
446            XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PROTO_PORT);
447            XmlUtil.writeNextValue(out, XML_TAG_PROTO, entry.getKey());
448            XmlUtil.writeNextValue(out, XML_TAG_PORTS, entry.getValue());
449            XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PROTO_PORT);
450        }
451        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_REQUIRED_PROTO_PORT_MAP);
452    }
453
454    /**
455     * Deserialize a {@link HomeSp} from an input stream.
456     *
457     * @param in The input stream to read data from
458     * @param outerTagDepth The tag depth of the current XML section
459     * @return {@link HomeSp}
460     * @throws XmlPullParserException
461     * @throws IOException
462     */
463    private static HomeSp deserializeHomeSP(XmlPullParser in, int outerTagDepth)
464            throws XmlPullParserException, IOException {
465        HomeSp homeSp = new HomeSp();
466        while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
467            String[] valueName = new String[1];
468            Object value = XmlUtil.readCurrentValue(in, valueName);
469            if (valueName[0] == null) {
470                throw new XmlPullParserException("Missing value name");
471            }
472            switch (valueName[0]) {
473                case XML_TAG_FQDN:
474                    homeSp.setFqdn((String) value);
475                    break;
476                case XML_TAG_FRIENDLY_NAME:
477                    homeSp.setFriendlyName((String) value);
478                    break;
479                case XML_TAG_ICON_URL:
480                    homeSp.setIconUrl((String) value);
481                    break;
482                case XML_TAG_HOME_NETWORK_IDS:
483                    homeSp.setHomeNetworkIds((Map<String, Long>) value);
484                    break;
485                case XML_TAG_MATCH_ALL_OIS:
486                    homeSp.setMatchAllOis((long[]) value);
487                    break;
488                case XML_TAG_MATCH_ANY_OIS:
489                    homeSp.setMatchAnyOis((long[]) value);
490                    break;
491                case XML_TAG_ROAMING_CONSORTIUM_OIS:
492                    homeSp.setRoamingConsortiumOis((long[]) value);
493                    break;
494                case XML_TAG_OTHER_HOME_PARTNERS:
495                    homeSp.setOtherHomePartners((String[]) value);
496                    break;
497                default:
498                    throw new XmlPullParserException("Unknown data under HomeSP: " + valueName[0]);
499            }
500        }
501        return homeSp;
502    }
503
504    /**
505     * Deserialize a {@link Credential} from an input stream.
506     *
507     * @param in The input stream to read data from
508     * @param outerTagDepth The tag depth of the current XML section
509     * @return {@link Credential}
510     * @throws XmlPullParserException
511     * @throws IOException
512     */
513    private static Credential deserializeCredential(XmlPullParser in, int outerTagDepth)
514            throws XmlPullParserException, IOException {
515        Credential credential = new Credential();
516        while (XmlUtils.nextElementWithin(in, outerTagDepth)) {
517            if (isValueElement(in)) {
518                // Value elements.
519                String[] name = new String[1];
520                Object value = XmlUtil.readCurrentValue(in, name);
521                switch (name[0]) {
522                    case XML_TAG_CREATION_TIME:
523                        credential.setCreationTimeInMillis((long) value);
524                        break;
525                    case XML_TAG_EXPIRATION_TIME:
526                        credential.setExpirationTimeInMillis((long) value);
527                        break;
528                    case XML_TAG_REALM:
529                        credential.setRealm((String) value);
530                        break;
531                    case XML_TAG_CHECK_AAA_SERVER_CERT_STATUS:
532                        credential.setCheckAaaServerCertStatus((boolean) value);
533                        break;
534                    default:
535                        throw new XmlPullParserException("Unknown value under Credential: "
536                            + name[0]);
537                }
538            } else {
539                // Subsection elements.
540                switch (in.getName()) {
541                    case XML_TAG_SECTION_HEADER_USER_CREDENTIAL:
542                        credential.setUserCredential(
543                                deserializeUserCredential(in, outerTagDepth + 1));
544                        break;
545                    case XML_TAG_SECTION_HEADER_CERT_CREDENTIAL:
546                        credential.setCertCredential(
547                                deserializeCertCredential(in, outerTagDepth + 1));
548                        break;
549                    case XML_TAG_SECTION_HEADER_SIM_CREDENTIAL:
550                        credential.setSimCredential(
551                                deserializeSimCredential(in, outerTagDepth + 1));
552                        break;
553                    default:
554                        throw new XmlPullParserException("Unknown section under Credential: "
555                                + in.getName());
556                }
557            }
558        }
559        return credential;
560    }
561
562    /**
563     * Deserialize a {@link Policy} from an input stream.
564     *
565     * @param in The input stream to read data from
566     * @param outerTagDepth The tag depth of the current XML section
567     * @return {@link Policy}
568     * @throws XmlPullParserException
569     * @throws IOException
570     */
571    private static Policy deserializePolicy(XmlPullParser in, int outerTagDepth)
572            throws XmlPullParserException, IOException {
573        Policy policy = new Policy();
574        while (XmlUtils.nextElementWithin(in, outerTagDepth)) {
575            if (isValueElement(in)) {
576                // Value elements.
577                String[] name = new String[1];
578                Object value = XmlUtil.readCurrentValue(in, name);
579                switch (name[0]) {
580                    case XML_TAG_MIN_HOME_DOWNLINK_BANDWIDTH:
581                        policy.setMinHomeDownlinkBandwidth((long) value);
582                        break;
583                    case XML_TAG_MIN_HOME_UPLINK_BANDWIDTH:
584                        policy.setMinHomeUplinkBandwidth((long) value);
585                        break;
586                    case XML_TAG_MIN_ROAMING_DOWNLINK_BANDWIDTH:
587                        policy.setMinRoamingDownlinkBandwidth((long) value);
588                        break;
589                    case XML_TAG_MIN_ROAMING_UPLINK_BANDWIDTH:
590                        policy.setMinRoamingUplinkBandwidth((long) value);
591                        break;
592                    case XML_TAG_EXCLUDED_SSID_LIST:
593                        policy.setExcludedSsidList((String[]) value);
594                        break;
595                    case XML_TAG_MAXIMUM_BSS_LOAD_VALUE:
596                        policy.setMaximumBssLoadValue((int) value);
597                        break;
598                }
599            } else {
600                // Subsection elements.
601                switch (in.getName()) {
602                    case XML_TAG_SECTION_HEADER_REQUIRED_PROTO_PORT_MAP:
603                        policy.setRequiredProtoPortMap(
604                                deserializeProtoPortMap(in, outerTagDepth + 1));
605                        break;
606                    case XML_TAG_SECTION_HEADER_POLICY_UPDATE:
607                        policy.setPolicyUpdate(deserializeUpdateParameter(in, outerTagDepth + 1));
608                        break;
609                    case XML_TAG_SECTION_HEADER_PREFERRED_ROAMING_PARTNER_LIST:
610                        policy.setPreferredRoamingPartnerList(
611                                deserializePreferredRoamingPartnerList(in, outerTagDepth + 1));
612                        break;
613                    default:
614                        throw new XmlPullParserException("Unknown section under Policy: "
615                                + in.getName());
616                }
617            }
618        }
619        return policy;
620    }
621
622    /**
623     * Deserialize a {@link android.net.wifi.hotspot2.pps.Credential.UserCredential} from an
624     * input stream.
625     *
626     * @param in The input stream to read data from
627     * @param outerTagDepth The tag depth of the current XML section
628     * @return {@link android.net.wifi.hotspot2.pps.Credential.UserCredential}
629     * @throws XmlPullParserException
630     * @throws IOException
631     */
632    private static Credential.UserCredential deserializeUserCredential(XmlPullParser in,
633            int outerTagDepth) throws XmlPullParserException, IOException {
634        Credential.UserCredential userCredential = new Credential.UserCredential();
635        while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
636            String[] valueName = new String[1];
637            Object value = XmlUtil.readCurrentValue(in, valueName);
638            if (valueName[0] == null) {
639                throw new XmlPullParserException("Missing value name");
640            }
641            switch (valueName[0]) {
642                case XML_TAG_USERNAME:
643                    userCredential.setUsername((String) value);
644                    break;
645                case XML_TAG_PASSWORD:
646                    userCredential.setPassword((String) value);
647                    break;
648                case XML_TAG_MACHINE_MANAGED:
649                    userCredential.setMachineManaged((boolean) value);
650                    break;
651                case XML_TAG_SOFT_TOKEN_APP:
652                    userCredential.setSoftTokenApp((String) value);
653                    break;
654                case XML_TAG_ABLE_TO_SHARE:
655                    userCredential.setAbleToShare((boolean) value);
656                    break;
657                case XML_TAG_EAP_TYPE:
658                    userCredential.setEapType((int) value);
659                    break;
660                case XML_TAG_NON_EAP_INNER_METHOD:
661                    userCredential.setNonEapInnerMethod((String) value);
662                    break;
663                default:
664                    throw new XmlPullParserException("Unknown value under UserCredential: "
665                            + valueName[0]);
666            }
667        }
668        return userCredential;
669    }
670
671    /**
672     * Deserialize a {@link android.net.wifi.hotspot2.pps.Credential.CertificateCredential}
673     * from an input stream.
674     *
675     * @param in The input stream to read data from
676     * @param outerTagDepth The tag depth of the current XML section
677     * @return {@link android.net.wifi.hotspot2.pps.Credential.CertificateCredential}
678     * @throws XmlPullParserException
679     * @throws IOException
680     */
681    private static Credential.CertificateCredential deserializeCertCredential(XmlPullParser in,
682            int outerTagDepth) throws XmlPullParserException, IOException {
683        Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
684        while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
685            String[] valueName = new String[1];
686            Object value = XmlUtil.readCurrentValue(in, valueName);
687            if (valueName[0] == null) {
688                throw new XmlPullParserException("Missing value name");
689            }
690            switch (valueName[0]) {
691                case XML_TAG_CERT_TYPE:
692                    certCredential.setCertType((String) value);
693                    break;
694                case XML_TAG_CERT_SHA256_FINGERPRINT:
695                    certCredential.setCertSha256Fingerprint((byte[]) value);
696                    break;
697                default:
698                    throw new XmlPullParserException("Unknown value under CertCredential: "
699                            + valueName[0]);
700            }
701        }
702        return certCredential;
703    }
704
705    /**
706     * Deserialize a {@link android.net.wifi.hotspot2.pps.Credential.SimCredential} from an
707     * input stream.
708     *
709     * @param in The input stream to read data from
710     * @param outerTagDepth The tag depth of the current XML section
711     * @return {@link android.net.wifi.hotspot2.pps.Credential.SimCredential}
712     * @throws XmlPullParserException
713     * @throws IOException
714     */
715    private static Credential.SimCredential deserializeSimCredential(XmlPullParser in,
716            int outerTagDepth) throws XmlPullParserException, IOException {
717        Credential.SimCredential simCredential = new Credential.SimCredential();
718        while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
719            String[] valueName = new String[1];
720            Object value = XmlUtil.readCurrentValue(in, valueName);
721            if (valueName[0] == null) {
722                throw new XmlPullParserException("Missing value name");
723            }
724            switch (valueName[0]) {
725                case XML_TAG_IMSI:
726                    simCredential.setImsi((String) value);
727                    break;
728                case XML_TAG_EAP_TYPE:
729                    simCredential.setEapType((int) value);
730                    break;
731                default:
732                    throw new XmlPullParserException("Unknown value under CertCredential: "
733                            + valueName[0]);
734            }
735        }
736        return simCredential;
737    }
738
739    /**
740     * Deserialize a list of {@link android.net.wifi.hotspot2.pps.Policy.RoamingPartner} from an
741     * input stream.
742     *
743     * @param in The input stream to read data from
744     * @param outerTagDepth The tag depth of the current XML section
745     * @return List of {@link android.net.wifi.hotspot2.pps.Policy.RoamingPartner}
746     * @throws XmlPullParserException
747     * @throws IOException
748     */
749    private static List<Policy.RoamingPartner> deserializePreferredRoamingPartnerList(
750            XmlPullParser in, int outerTagDepth) throws XmlPullParserException, IOException {
751        List<Policy.RoamingPartner> roamingPartnerList = new ArrayList<>();
752        while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_SECTION_HEADER_ROAMING_PARTNER,
753                outerTagDepth)) {
754            roamingPartnerList.add(deserializeRoamingPartner(in, outerTagDepth + 1));
755        }
756        return roamingPartnerList;
757    }
758
759    /**
760     * Deserialize a {@link android.net.wifi.hotspot2.pps.Policy.RoamingPartner} from an input
761     * stream.
762     *
763     * @param in The input stream to read data from
764     * @param outerTagDepth The tag depth of the current XML section
765     * @return {@link android.net.wifi.hotspot2.pps.Policy.RoamingPartner}
766     * @throws XmlPullParserException
767     * @throws IOException
768     */
769    private static Policy.RoamingPartner deserializeRoamingPartner(XmlPullParser in,
770            int outerTagDepth) throws XmlPullParserException, IOException {
771        Policy.RoamingPartner partner = new Policy.RoamingPartner();
772        while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
773            String[] valueName = new String[1];
774            Object value = XmlUtil.readCurrentValue(in, valueName);
775            if (valueName[0] == null) {
776                throw new XmlPullParserException("Missing value name");
777            }
778            switch (valueName[0]) {
779                case XML_TAG_FQDN:
780                    partner.setFqdn((String) value);
781                    break;
782                case XML_TAG_FQDN_EXACT_MATCH:
783                    partner.setFqdnExactMatch((boolean) value);
784                    break;
785                case XML_TAG_PRIORITY:
786                    partner.setPriority((int) value);
787                    break;
788                case XML_TAG_COUNTRIES:
789                    partner.setCountries((String) value);
790                    break;
791                default:
792                    throw new XmlPullParserException("Unknown value under RoamingPartner: "
793                            + valueName[0]);
794            }
795        }
796        return partner;
797    }
798
799    /**
800     * Deserialize a {@link UpdateParameter} from an input stream.
801     *
802     * @param in The input stream to read data from
803     * @param outerTagDepth The tag depth of the current XML section
804     * @return {@link UpdateParameter}
805     * @throws XmlPullParserException
806     * @throws IOException
807     */
808    private static UpdateParameter deserializeUpdateParameter(XmlPullParser in,
809            int outerTagDepth) throws XmlPullParserException, IOException {
810        UpdateParameter param = new UpdateParameter();
811        while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
812            String[] valueName = new String[1];
813            Object value = XmlUtil.readCurrentValue(in, valueName);
814            if (valueName[0] == null) {
815                throw new XmlPullParserException("Missing value name");
816            }
817            switch (valueName[0]) {
818                case XML_TAG_UPDATE_INTERVAL:
819                    param.setUpdateIntervalInMinutes((long) value);
820                    break;
821                case XML_TAG_UPDATE_METHOD:
822                    param.setUpdateMethod((String) value);
823                    break;
824                case XML_TAG_RESTRICTION:
825                    param.setRestriction((String) value);
826                    break;
827                case XML_TAG_SERVER_URI:
828                    param.setServerUri((String) value);
829                    break;
830                case XML_TAG_USERNAME:
831                    param.setUsername((String) value);
832                    break;
833                case XML_TAG_PASSWORD:
834                    param.setBase64EncodedPassword((String) value);
835                    break;
836                case XML_TAG_TRUST_ROOT_CERT_URL:
837                    param.setTrustRootCertUrl((String) value);
838                    break;
839                case XML_TAG_TRUST_ROOT_CERT_SHA256_FINGERPRINT:
840                    param.setTrustRootCertSha256Fingerprint((byte[]) value);
841                    break;
842                default:
843                    throw new XmlPullParserException("Unknown value under UpdateParameter: "
844                            + valueName[0]);
845            }
846        }
847        return param;
848    }
849
850    /**
851     * Deserialize a Protocol-Port map from an input stream.
852     *
853     * @param in The input stream to read data from
854     * @param outerTagDepth The tag depth of the current XML section
855     * @return Proocol-Port map
856     * @throws XmlPullParserException
857     * @throws IOException
858     */
859    private static Map<Integer, String> deserializeProtoPortMap(XmlPullParser in,
860            int outerTagDepth) throws XmlPullParserException, IOException {
861        Map<Integer, String> protoPortMap = new HashMap<>();
862        while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_SECTION_HEADER_PROTO_PORT,
863                outerTagDepth)) {
864            int proto = (int) XmlUtil.readNextValueWithName(in, XML_TAG_PROTO);
865            String ports = (String) XmlUtil.readNextValueWithName(in, XML_TAG_PORTS);
866            protoPortMap.put(proto, ports);
867        }
868        return protoPortMap;
869    }
870
871    /**
872     * Determine if the current element is a value or a section.  The "name" attribute of the
873     * element is used as the indicator, when it is present, the element is considered a value
874     * element.
875     *
876     * Value element:
877     * <int name="test">12</int>
878     *
879     * Section element:
880     * <Test>
881     * ...
882     * </Test>
883     *
884     * @param in XML input stream
885     * @return true if the current element is a value
886     */
887    private static boolean isValueElement(XmlPullParser in) {
888        return in.getAttributeValue(null, "name") != null;
889    }
890}
891