MOManager.java revision 1c03d75c73b9f5fa24a795a0d546f4f56b82ab9b
1package com.android.server.wifi.hotspot2.omadm;
2
3import android.util.Base64;
4import android.util.Log;
5
6import com.android.server.wifi.anqp.eap.EAP;
7import com.android.server.wifi.anqp.eap.EAPMethod;
8import com.android.server.wifi.anqp.eap.ExpandedEAPMethod;
9import com.android.server.wifi.anqp.eap.InnerAuthEAP;
10import com.android.server.wifi.anqp.eap.NonEAPInnerAuth;
11import com.android.server.wifi.hotspot2.Utils;
12import com.android.server.wifi.hotspot2.pps.Credential;
13import com.android.server.wifi.hotspot2.pps.HomeSP;
14
15import org.xml.sax.SAXException;
16
17import java.io.BufferedInputStream;
18import java.io.BufferedOutputStream;
19import java.io.File;
20import java.io.FileInputStream;
21import java.io.FileOutputStream;
22import java.io.IOException;
23import java.nio.charset.StandardCharsets;
24import java.text.DateFormat;
25import java.text.ParseException;
26import java.text.SimpleDateFormat;
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.Collection;
30import java.util.Collections;
31import java.util.Date;
32import java.util.HashMap;
33import java.util.HashSet;
34import java.util.List;
35import java.util.Map;
36import java.util.Set;
37import java.util.TimeZone;
38
39/**
40 * Handles provisioning of PerProviderSubscription data.
41 */
42public class MOManager {
43
44    public static final String TAG_AAAServerTrustRoot = "AAAServerTrustRoot";
45    public static final String TAG_AbleToShare = "AbleToShare";
46    public static final String TAG_CertificateType = "CertificateType";
47    public static final String TAG_CertSHA256Fingerprint = "CertSHA256Fingerprint";
48    public static final String TAG_CertURL = "CertURL";
49    public static final String TAG_CheckAAAServerCertStatus = "CheckAAAServerCertStatus";
50    public static final String TAG_Country = "Country";
51    public static final String TAG_CreationDate = "CreationDate";
52    public static final String TAG_Credential = "Credential";
53    public static final String TAG_CredentialPriority = "CredentialPriority";
54    public static final String TAG_DataLimit = "DataLimit";
55    public static final String TAG_DigitalCertificate = "DigitalCertificate";
56    public static final String TAG_DLBandwidth = "DLBandwidth";
57    public static final String TAG_EAPMethod = "EAPMethod";
58    public static final String TAG_EAPType = "EAPType";
59    public static final String TAG_ExpirationDate = "ExpirationDate";
60    public static final String TAG_Extension = "Extension";
61    public static final String TAG_FQDN = "FQDN";
62    public static final String TAG_FQDN_Match = "FQDN_Match";
63    public static final String TAG_FriendlyName = "FriendlyName";
64    public static final String TAG_HESSID = "HESSID";
65    public static final String TAG_HomeOI = "HomeOI";
66    public static final String TAG_HomeOIList = "HomeOIList";
67    public static final String TAG_HomeOIRequired = "HomeOIRequired";
68    public static final String TAG_HomeSP = "HomeSP";
69    public static final String TAG_IconURL = "IconURL";
70    public static final String TAG_IMSI = "IMSI";
71    public static final String TAG_InnerEAPType = "InnerEAPType";
72    public static final String TAG_InnerMethod = "InnerMethod";
73    public static final String TAG_InnerVendorID = "InnerVendorID";
74    public static final String TAG_InnerVendorType = "InnerVendorType";
75    public static final String TAG_IPProtocol = "IPProtocol";
76    public static final String TAG_MachineManaged = "MachineManaged";
77    public static final String TAG_MaximumBSSLoadValue = "MaximumBSSLoadValue";
78    public static final String TAG_MinBackhaulThreshold = "MinBackhaulThreshold";
79    public static final String TAG_NetworkID = "NetworkID";
80    public static final String TAG_NetworkType = "NetworkType";
81    public static final String TAG_Other = "Other";
82    public static final String TAG_OtherHomePartners = "OtherHomePartners";
83    public static final String TAG_Password = "Password";
84    public static final String TAG_PerProviderSubscription = "PerProviderSubscription";
85    public static final String TAG_Policy = "Policy";
86    public static final String TAG_PolicyUpdate = "PolicyUpdate";
87    public static final String TAG_PortNumber = "PortNumber";
88    public static final String TAG_PreferredRoamingPartnerList = "PreferredRoamingPartnerList";
89    public static final String TAG_Priority = "Priority";
90    public static final String TAG_Realm = "Realm";
91    public static final String TAG_RequiredProtoPortTuple = "RequiredProtoPortTuple";
92    public static final String TAG_Restriction = "Restriction";
93    public static final String TAG_RoamingConsortiumOI = "RoamingConsortiumOI";
94    public static final String TAG_SIM = "SIM";
95    public static final String TAG_SoftTokenApp = "SoftTokenApp";
96    public static final String TAG_SPExclusionList = "SPExclusionList";
97    public static final String TAG_SSID = "SSID";
98    public static final String TAG_StartDate = "StartDate";
99    public static final String TAG_SubscriptionParameters = "SubscriptionParameters";
100    public static final String TAG_SubscriptionUpdate = "SubscriptionUpdate";
101    public static final String TAG_TimeLimit = "TimeLimit";
102    public static final String TAG_TrustRoot = "TrustRoot";
103    public static final String TAG_TypeOfSubscription = "TypeOfSubscription";
104    public static final String TAG_ULBandwidth = "ULBandwidth";
105    public static final String TAG_UpdateIdentifier = "UpdateIdentifier";
106    public static final String TAG_UpdateInterval = "UpdateInterval";
107    public static final String TAG_UpdateMethod = "UpdateMethod";
108    public static final String TAG_URI = "URI";
109    public static final String TAG_UsageLimits = "UsageLimits";
110    public static final String TAG_UsageTimePeriod = "UsageTimePeriod";
111    public static final String TAG_Username = "Username";
112    public static final String TAG_UsernamePassword = "UsernamePassword";
113    public static final String TAG_VendorId = "VendorId";
114    public static final String TAG_VendorType = "VendorType";
115
116    private static final DateFormat DTFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
117
118    static {
119        DTFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
120    }
121
122    private final File mPpsFile;
123    private final Map<String, HomeSP> mSPs;
124
125    public MOManager(File ppsFile) {
126        mPpsFile = ppsFile;
127        mSPs = new HashMap<>();
128    }
129
130    public File getPpsFile() {
131        return mPpsFile;
132    }
133
134    public Map<String, HomeSP> getLoadedSPs() {
135        return mSPs;
136    }
137
138    public List<HomeSP> loadAllSPs() throws IOException {
139
140        if (!mPpsFile.exists()) {
141            return Collections.emptyList();
142        }
143
144        try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(mPpsFile))) {
145            MOTree moTree = MOTree.unmarshal(in);
146            mSPs.clear();
147            if (moTree == null) {
148                return Collections.emptyList();     // Empty file
149            }
150
151            List<HomeSP> sps = buildSPs(moTree);
152            if (sps != null) {
153                for (HomeSP sp : sps) {
154                    if (mSPs.put(sp.getFQDN(), sp) != null) {
155                        throw new OMAException("Multiple SPs for FQDN '" + sp.getFQDN() + "'");
156                    } else {
157                        Log.d(Utils.hs2LogTag(getClass()), "retrieved " + sp.getFQDN() + " from PPS");
158                    }
159                }
160                return sps;
161
162            } else {
163                throw new OMAException("Failed to build HomeSP");
164            }
165        }
166    }
167
168    public static HomeSP buildSP(String xml) throws IOException, SAXException {
169        OMAParser omaParser = new OMAParser();
170        MOTree tree = omaParser.parse(xml, OMAConstants.LOC_PPS + ":1.0");
171        List<HomeSP> spList = buildSPs(tree);
172        if (spList.size() != 1) {
173            throw new OMAException("Expected exactly one HomeSP, got " + spList.size());
174        }
175        return spList.iterator().next();
176    }
177
178    public HomeSP addSP(String xml) throws IOException, SAXException {
179        OMAParser omaParser = new OMAParser();
180        MOTree tree = omaParser.parse(xml, OMAConstants.LOC_PPS + ":1.0");
181        List<HomeSP> spList = buildSPs(tree);
182        if (spList.size() != 1) {
183            throw new OMAException("Expected exactly one HomeSP, got " + spList.size());
184        }
185        HomeSP sp = spList.iterator().next();
186        String fqdn = sp.getFQDN();
187        if (mSPs.put(fqdn, sp) != null) {
188            throw new OMAException("SP " + fqdn + " already exists");
189        }
190
191        BufferedOutputStream out = null;
192        try {
193            out = new BufferedOutputStream(new FileOutputStream(mPpsFile, true));
194            tree.marshal(out);
195            out.flush();
196        } finally {
197            if (out != null) {
198                try {
199                    out.close();
200                } catch (IOException ioe) {
201                    /**/
202                }
203            }
204        }
205
206        return sp;
207    }
208
209    public void saveAllSps(Collection<HomeSP> homeSPs) throws IOException {
210
211        Map<String, HomeSP> obsolete = new HashMap<>(mSPs);
212        List<HomeSP> resultSet = new ArrayList<>(homeSPs.size());
213        int additions = 0;
214
215        for (HomeSP newSP : homeSPs) {
216            HomeSP existing = obsolete.remove(newSP.getFQDN());
217            if (existing == null) {
218                resultSet.add(newSP);
219                additions++;
220            }
221            else {
222                resultSet.add(existing);
223            }
224            Log.d("HSXX", "From wpa: " + newSP.getCredential().hasDisregardPassword());
225        }
226
227        if (!obsolete.isEmpty() || additions > 0) {
228            Log.d(Utils.hs2LogTag(getClass()), String.format("MO change: %s -> %s: %s",
229                    fqdnList(mSPs.values()), fqdnList(homeSPs), fqdnList(resultSet)));
230            rewriteMO(resultSet, mSPs, mPpsFile);
231        }
232        else {
233            Log.d(Utils.hs2LogTag(getClass()), "Not persisting MO");
234        }
235    }
236
237    public void updateAndSaveAllSps(Collection<HomeSP> homeSPs) throws IOException {
238
239        boolean dirty = false;
240        List<HomeSP> newSet = new ArrayList<>(homeSPs.size());
241
242        Map<String, HomeSP> spClone = new HashMap<>(mSPs);
243        for (HomeSP homeSP : homeSPs) {
244            Log.d(Utils.hs2LogTag(getClass()), "Passed HomeSP: " + homeSP);
245            HomeSP existing = spClone.remove(homeSP.getFQDN());
246            if (existing == null) {
247                dirty = true;
248                newSet.add(homeSP);
249                Log.d(Utils.hs2LogTag(getClass()), "New HomeSP");
250            }
251            else if (!homeSP.deepEquals(existing)) {
252                dirty = true;
253                newSet.add(homeSP.getClone(existing.getCredential().getPassword()));
254                Log.d(Utils.hs2LogTag(getClass()), "Non-equal HomeSP: " + existing);
255            }
256            else {
257                newSet.add(existing);
258                Log.d(Utils.hs2LogTag(getClass()), "Keeping HomeSP: " + existing);
259            }
260        }
261
262        Log.d(Utils.hs2LogTag(getClass()),
263                String.format("Saving all SPs (%s): current %s (%d), new %s (%d)",
264                dirty ? "dirty" : "clean",
265                fqdnList(mSPs.values()), mSPs.size(),
266                fqdnList(newSet), newSet.size()));
267
268        if (!dirty && spClone.isEmpty()) {
269            Log.d(Utils.hs2LogTag(getClass()), "Not persisting");
270            return;
271        }
272
273        rewriteMO(newSet, mSPs, mPpsFile);
274    }
275
276    private static void rewriteMO(Collection<HomeSP> homeSPs, Map<String, HomeSP> current, File f)
277            throws IOException {
278
279        current.clear();
280
281        OMAConstructed ppsNode = new OMAConstructed(null, TAG_PerProviderSubscription, null);
282        int instance = 0;
283        for (HomeSP homeSP : homeSPs) {
284            buildHomeSPTree(homeSP, ppsNode, instance++);
285            current.put(homeSP.getFQDN(), homeSP);
286        }
287
288        MOTree tree = new MOTree(OMAConstants.LOC_PPS + ":1.0", "1.2", ppsNode);
289        try (BufferedOutputStream out =
290                     new BufferedOutputStream(new FileOutputStream(f, false))) {
291            tree.marshal(out);
292            out.flush();
293        }
294    }
295
296    private static String fqdnList(Collection<HomeSP> sps) {
297        StringBuilder sb = new StringBuilder();
298        boolean first = true;
299        for (HomeSP sp : sps) {
300            if (first) {
301                first = false;
302            }
303            else {
304                sb.append(", ");
305            }
306            sb.append(sp.getFQDN());
307        }
308        return sb.toString();
309    }
310
311    private static void buildHomeSPTree(HomeSP homeSP, OMAConstructed root, int spInstance)
312            throws IOException {
313        OMANode providerSubNode = root.addChild(getInstanceString(spInstance), null, null, null);
314
315        // The HomeSP:
316        OMANode homeSpNode = providerSubNode.addChild(TAG_HomeSP, null, null, null);
317        if (!homeSP.getSSIDs().isEmpty()) {
318            OMAConstructed nwkIDNode =
319                    (OMAConstructed) homeSpNode.addChild(TAG_NetworkID, null, null, null);
320            int instance = 0;
321            for (Map.Entry<String, Long> entry : homeSP.getSSIDs().entrySet()) {
322                OMAConstructed inode =
323                        (OMAConstructed) nwkIDNode.addChild(getInstanceString(instance++), null, null, null);
324                inode.addChild(TAG_SSID, null, entry.getKey(), null);
325                if (entry.getValue() != null) {
326                    inode.addChild(TAG_HESSID, null, String.format("%012x", entry.getValue()), null);
327                }
328            }
329        }
330
331        homeSpNode.addChild(TAG_FriendlyName, null, homeSP.getFriendlyName(), null);
332
333        if (homeSP.getIconURL() != null) {
334            homeSpNode.addChild(TAG_IconURL, null, homeSP.getIconURL(), null);
335        }
336
337        homeSpNode.addChild(TAG_FQDN, null, homeSP.getFQDN(), null);
338
339        if (!homeSP.getMatchAllOIs().isEmpty() || !homeSP.getMatchAnyOIs().isEmpty()) {
340            OMAConstructed homeOIList =
341                    (OMAConstructed) homeSpNode.addChild(TAG_HomeOIList, null, null, null);
342
343            int instance = 0;
344            for (Long oi : homeSP.getMatchAllOIs()) {
345                OMAConstructed inode =
346                        (OMAConstructed) homeOIList.addChild(getInstanceString(instance++),
347                                null, null, null);
348                inode.addChild(TAG_HomeOI, null, String.format("%x", oi), null);
349                inode.addChild(TAG_HomeOIRequired, null, "TRUE", null);
350            }
351            for (Long oi : homeSP.getMatchAnyOIs()) {
352                OMAConstructed inode =
353                        (OMAConstructed) homeOIList.addChild(getInstanceString(instance++),
354                                null, null, null);
355                inode.addChild(TAG_HomeOI, null, String.format("%x", oi), null);
356                inode.addChild(TAG_HomeOIRequired, null, "FALSE", null);
357            }
358        }
359
360        if (!homeSP.getOtherHomePartners().isEmpty()) {
361            OMAConstructed otherPartners =
362                    (OMAConstructed) homeSpNode.addChild(TAG_OtherHomePartners, null, null, null);
363            int instance = 0;
364            for (String fqdn : homeSP.getOtherHomePartners()) {
365                OMAConstructed inode =
366                        (OMAConstructed) otherPartners.addChild(getInstanceString(instance++),
367                                null, null, null);
368                inode.addChild(TAG_FQDN, null, fqdn, null);
369            }
370        }
371
372        if (!homeSP.getRoamingConsortiums().isEmpty()) {
373            homeSpNode.addChild(TAG_RoamingConsortiumOI, null, getRCList(homeSP.getRoamingConsortiums()), null);
374        }
375
376        // The Credential:
377        OMANode credentialNode = providerSubNode.addChild(TAG_Credential, null, null, null);
378        Credential cred = homeSP.getCredential();
379        EAPMethod method = cred.getEAPMethod();
380
381        if (cred.getCtime() > 0) {
382            credentialNode.addChild(TAG_CreationDate,
383                    null, DTFormat.format(new Date(cred.getCtime())), null);
384        }
385        if (cred.getExpTime() > 0) {
386            credentialNode.addChild(TAG_ExpirationDate,
387                    null, DTFormat.format(new Date(cred.getExpTime())), null);
388        }
389
390        if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_SIM
391                || method.getEAPMethodID() == EAP.EAPMethodID.EAP_AKA
392                || method.getEAPMethodID() == EAP.EAPMethodID.EAP_AKAPrim) {
393
394            OMANode simNode = credentialNode.addChild(TAG_SIM, null, null, null);
395            simNode.addChild(TAG_IMSI, null, cred.getImsi(), null);
396            simNode.addChild(TAG_EAPType, null,
397                    Integer.toString(EAP.mapEAPMethod(method.getEAPMethodID())), null);
398
399        } else if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_TTLS) {
400
401            OMANode unpNode = credentialNode.addChild(TAG_UsernamePassword, null, null, null);
402            unpNode.addChild(TAG_Username, null, cred.getUserName(), null);
403            unpNode.addChild(TAG_Password, null,
404                    Base64.encodeToString(cred.getPassword().getBytes(StandardCharsets.UTF_8),
405                            Base64.DEFAULT), null);
406            OMANode eapNode = unpNode.addChild(TAG_EAPMethod, null, null, null);
407            eapNode.addChild(TAG_EAPType, null,
408                    Integer.toString(EAP.mapEAPMethod(method.getEAPMethodID())), null);
409            eapNode.addChild(TAG_InnerMethod, null,
410                    ((NonEAPInnerAuth) method.getAuthParam()).getOMAtype(), null);
411
412        } else if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_TLS) {
413
414            OMANode certNode = credentialNode.addChild(TAG_DigitalCertificate, null, null, null);
415            certNode.addChild(TAG_CertificateType, null, Credential.CertTypeX509, null);
416            certNode.addChild(TAG_CertSHA256Fingerprint, null,
417                    Utils.toHex(cred.getFingerPrint()), null);
418
419        } else {
420            throw new OMAException("Invalid credential on " + homeSP.getFQDN());
421        }
422
423        credentialNode.addChild(TAG_Realm, null, cred.getRealm(), null);
424
425        // !!! Note: This node defines CRL checking through OSCP, I suspect we won't be able
426        // to do that so it is commented out:
427        //credentialNode.addChild(TAG_CheckAAAServerCertStatus, null, "TRUE", null);
428    }
429
430    private static String getInstanceString(int instance) {
431        return "i" + instance;
432    }
433
434    private static String getRCList(Collection<Long> rcs) {
435        StringBuilder builder = new StringBuilder();
436        boolean first = true;
437        for (Long roamingConsortium : rcs) {
438            if (first) {
439                first = false;
440            }
441            else {
442                builder.append(',');
443            }
444            builder.append(String.format("%x", roamingConsortium));
445        }
446        return builder.toString();
447    }
448
449    private static List<HomeSP> buildSPs(MOTree moTree) throws OMAException {
450        OMAConstructed spList;
451        if (moTree.getRoot().getName().equals(TAG_PerProviderSubscription)) {
452            // The PPS file is rooted at PPS instead of MgmtTree to conserve space
453            spList = moTree.getRoot();
454        }
455        else {
456            List<String> spPath = Arrays.asList(TAG_PerProviderSubscription);
457            spList = moTree.getRoot().getListValue(spPath.iterator());
458        }
459
460        List<HomeSP> homeSPs = new ArrayList<>();
461
462        if (spList == null) {
463            return homeSPs;
464        }
465        for (OMANode spRoot : spList.getChildren()) {
466            homeSPs.add(buildHomeSP(spRoot));
467        }
468
469        return homeSPs;
470    }
471
472    private static HomeSP buildHomeSP(OMANode ppsRoot) throws OMAException {
473        OMANode spRoot = ppsRoot.getChild(TAG_HomeSP);
474
475        String fqdn = spRoot.getScalarValue(Arrays.asList(TAG_FQDN).iterator());
476        String friendlyName = spRoot.getScalarValue(Arrays.asList(TAG_FriendlyName).iterator());
477        String iconURL = spRoot.getScalarValue(Arrays.asList(TAG_IconURL).iterator());
478
479        HashSet<Long> roamingConsortiums = new HashSet<>();
480        String oiString = spRoot.getScalarValue(Arrays.asList(TAG_RoamingConsortiumOI).iterator());
481        if (oiString != null) {
482            for (String oi : oiString.split(",")) {
483                roamingConsortiums.add(Long.parseLong(oi.trim(), 16));
484            }
485        }
486
487        Map<String, Long> ssids = new HashMap<>();
488
489        OMANode ssidListNode = spRoot.getListValue(Arrays.asList(TAG_NetworkID).iterator());
490        if (ssidListNode != null) {
491            for (OMANode ssidRoot : ssidListNode.getChildren()) {
492                OMANode hessidNode = ssidRoot.getChild(TAG_HESSID);
493                ssids.put(ssidRoot.getChild(TAG_SSID).getValue(), getMac(hessidNode));
494            }
495        }
496
497        Set<Long> matchAnyOIs = new HashSet<>();
498        List<Long> matchAllOIs = new ArrayList<>();
499        OMANode homeOIListNode = spRoot.getListValue(Arrays.asList(TAG_HomeOIList).iterator());
500        if (homeOIListNode != null) {
501            for (OMANode homeOIRoot : homeOIListNode.getChildren()) {
502                String homeOI = homeOIRoot.getChild(TAG_HomeOI).getValue();
503                if (Boolean.parseBoolean(homeOIRoot.getChild(TAG_HomeOIRequired).getValue())) {
504                    matchAllOIs.add(Long.parseLong(homeOI, 16));
505                } else {
506                    matchAnyOIs.add(Long.parseLong(homeOI, 16));
507                }
508            }
509        }
510
511        Set<String> otherHomePartners = new HashSet<>();
512        OMANode otherListNode =
513                spRoot.getListValue(Arrays.asList(TAG_OtherHomePartners).iterator());
514        if (otherListNode != null) {
515            for (OMANode fqdnNode : otherListNode.getChildren()) {
516                otherHomePartners.add(fqdnNode.getChild(TAG_FQDN).getValue());
517            }
518        }
519
520        Credential credential = buildCredential(ppsRoot.getChild(TAG_Credential));
521
522        return new HomeSP(ssids, fqdn, roamingConsortiums, otherHomePartners,
523                matchAnyOIs, matchAllOIs, friendlyName, iconURL, credential);
524    }
525
526    private static Credential buildCredential(OMANode credNode) throws OMAException {
527        long ctime = getTime(credNode.getChild(TAG_CreationDate));
528        long expTime = getTime(credNode.getChild(TAG_ExpirationDate));
529        String realm = getString(credNode.getChild(TAG_Realm));
530        boolean checkAAACert = getBoolean(credNode.getChild(TAG_CheckAAAServerCertStatus));
531
532        OMANode unNode = credNode.getChild(TAG_UsernamePassword);
533        OMANode certNode = credNode.getChild(TAG_DigitalCertificate);
534        OMANode simNode = credNode.getChild(TAG_SIM);
535
536        int alternatives = 0;
537        alternatives += unNode != null ? 1 : 0;
538        alternatives += certNode != null ? 1 : 0;
539        alternatives += simNode != null ? 1 : 0;
540        if (alternatives != 1) {
541            throw new OMAException("Expected exactly one credential type, got " + alternatives);
542        }
543
544        if (unNode != null) {
545            String userName = getString(unNode.getChild(TAG_Username));
546            String password = getString(unNode.getChild(TAG_Password));
547            boolean machineManaged = getBoolean(unNode.getChild(TAG_MachineManaged));
548            String softTokenApp = getString(unNode.getChild(TAG_SoftTokenApp));
549            boolean ableToShare = getBoolean(unNode.getChild(TAG_AbleToShare));
550
551            OMANode eapMethodNode = unNode.getChild(TAG_EAPMethod);
552            int eapID = getInteger(eapMethodNode.getChild(TAG_EAPType));
553
554            EAP.EAPMethodID eapMethodID = EAP.mapEAPMethod(eapID);
555            if (eapMethodID == null) {
556                throw new OMAException("Unknown EAP method: " + eapID);
557            }
558
559            Long vid = getOptionalInteger(eapMethodNode.getChild(TAG_VendorId));
560            Long vtype = getOptionalInteger(eapMethodNode.getChild(TAG_VendorType));
561            Long innerEAPType = getOptionalInteger(eapMethodNode.getChild(TAG_InnerEAPType));
562            EAP.EAPMethodID innerEAPMethod = null;
563            if (innerEAPType != null) {
564                innerEAPMethod = EAP.mapEAPMethod(innerEAPType.intValue());
565                if (innerEAPMethod == null) {
566                    throw new OMAException("Bad inner EAP method: " + innerEAPType);
567                }
568            }
569
570            Long innerVid = getOptionalInteger(eapMethodNode.getChild(TAG_InnerVendorID));
571            Long innerVtype = getOptionalInteger(eapMethodNode.getChild(TAG_InnerVendorType));
572            String innerNonEAPMethod = getString(eapMethodNode.getChild(TAG_InnerMethod));
573
574            EAPMethod eapMethod;
575            if (innerEAPMethod != null) {
576                eapMethod = new EAPMethod(eapMethodID, new InnerAuthEAP(innerEAPMethod));
577            } else if (vid != null) {
578                eapMethod = new EAPMethod(eapMethodID,
579                        new ExpandedEAPMethod(EAP.AuthInfoID.ExpandedEAPMethod,
580                                vid.intValue(), vtype));
581            } else if (innerVid != null) {
582                eapMethod =
583                        new EAPMethod(eapMethodID, new ExpandedEAPMethod(EAP.AuthInfoID
584                                .ExpandedInnerEAPMethod, innerVid.intValue(), innerVtype));
585            } else if (innerNonEAPMethod != null) {
586                eapMethod = new EAPMethod(eapMethodID, new NonEAPInnerAuth(innerNonEAPMethod));
587            } else {
588                throw new OMAException("Incomplete set of EAP parameters");
589            }
590
591            return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, userName,
592                    password, machineManaged, softTokenApp, ableToShare);
593        }
594        if (certNode != null) {
595            try {
596                String certTypeString = getString(certNode.getChild(TAG_CertificateType));
597                byte[] fingerPrint = getOctets(certNode.getChild(TAG_CertSHA256Fingerprint));
598
599                EAPMethod eapMethod = new EAPMethod(EAP.EAPMethodID.EAP_TLS, null);
600
601                return new Credential(ctime, expTime, realm, checkAAACert, eapMethod,
602                        Credential.mapCertType(certTypeString), fingerPrint);
603            }
604            catch (NumberFormatException nfe) {
605                throw new OMAException("Bad hex string: " + nfe.toString());
606            }
607        }
608        if (simNode != null) {
609
610            String imsi = getString(simNode.getChild(TAG_IMSI));
611            EAPMethod eapMethod =
612                    new EAPMethod(EAP.mapEAPMethod(getInteger(simNode.getChild(TAG_EAPType))),
613                            null);
614
615            return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, imsi);
616        }
617        throw new OMAException("Missing credential parameters");
618    }
619
620    private static boolean getBoolean(OMANode boolNode) {
621        return boolNode != null && Boolean.parseBoolean(boolNode.getValue());
622    }
623
624    private static String getString(OMANode stringNode) {
625        return stringNode != null ? stringNode.getValue() : null;
626    }
627
628    private static int getInteger(OMANode intNode) throws OMAException {
629        if (intNode == null) {
630            throw new OMAException("Missing integer value");
631        }
632        try {
633            return Integer.parseInt(intNode.getValue());
634        } catch (NumberFormatException nfe) {
635            throw new OMAException("Invalid integer: " + intNode.getValue());
636        }
637    }
638
639    private static Long getMac(OMANode macNode) throws OMAException {
640        if (macNode == null) {
641            return null;
642        }
643        try {
644            return Long.parseLong(macNode.getValue(), 16);
645        } catch (NumberFormatException nfe) {
646            throw new OMAException("Invalid MAC: " + macNode.getValue());
647        }
648    }
649
650    private static Long getOptionalInteger(OMANode intNode) throws OMAException {
651        if (intNode == null) {
652            return null;
653        }
654        try {
655            return Long.parseLong(intNode.getValue());
656        } catch (NumberFormatException nfe) {
657            throw new OMAException("Invalid integer: " + intNode.getValue());
658        }
659    }
660
661    private static long getTime(OMANode timeNode) throws OMAException {
662        if (timeNode == null) {
663            return Utils.UNSET_TIME;
664        }
665        String timeText = timeNode.getValue();
666        try {
667            Date date = DTFormat.parse(timeText);
668            return date.getTime();
669        } catch (ParseException pe) {
670            throw new OMAException("Badly formatted time: " + timeText);
671        }
672    }
673
674    private static byte[] getOctets(OMANode octetNode) throws OMAException {
675        if (octetNode == null) {
676            throw new OMAException("Missing byte value");
677        }
678        return Utils.hexToBytes(octetNode.getValue());
679    }
680}
681