MOManager.java revision 820d73615f338d6c71f2d75aba0ad8410e9eed3e
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.HS20_TAG, "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        boolean dirty = false;
212        List<HomeSP> newSet = new ArrayList<>(homeSPs.size());
213
214        Map<String, HomeSP> spClone = new HashMap<>(mSPs);
215        for (HomeSP homeSP : homeSPs) {
216            HomeSP existing = spClone.remove(homeSP.getFQDN());
217            if (existing == null) {
218                dirty = true;
219                newSet.add(homeSP);
220            }
221            else if (!homeSP.deepEquals(existing)) {
222                dirty = true;
223                newSet.add(homeSP.getClone(existing.getCredential().getPassword()));
224                Log.d(Utils.HS20_TAG, "Non-equal HomeSP");
225            }
226            else {
227                newSet.add(existing);
228            }
229        }
230
231        Log.d(Utils.HS20_TAG, String.format("Saving all SPs (%s): current %s (%d), new %s (%d)",
232                dirty ? "dirty" : "clean",
233                fqdnList(mSPs.values()), mSPs.size(),
234                fqdnList(homeSPs), homeSPs.size()));
235
236        if (!dirty && spClone.isEmpty()) {
237            return;
238        }
239
240        mSPs.clear();
241
242        OMAConstructed ppsNode = new OMAConstructed(null, TAG_PerProviderSubscription, null);
243        int instance = 0;
244        for (HomeSP homeSP : newSet) {
245            buildHomeSPTree(homeSP, ppsNode, instance++);
246            mSPs.put(homeSP.getFQDN(), homeSP);
247        }
248
249        MOTree tree = new MOTree(OMAConstants.LOC_PPS + ":1.0", "1.2", ppsNode);
250        try (BufferedOutputStream out =
251                     new BufferedOutputStream(new FileOutputStream(mPpsFile, true))) {
252            tree.marshal(out);
253            out.flush();
254        }
255    }
256
257    private static String fqdnList(Collection<HomeSP> sps) {
258        StringBuilder sb = new StringBuilder();
259        boolean first = true;
260        for (HomeSP sp : sps) {
261            if (first) {
262                first = false;
263            }
264            else {
265                sb.append(", ");
266            }
267            sb.append(sp.getFQDN());
268        }
269        return sb.toString();
270    }
271
272    private static void buildHomeSPTree(HomeSP homeSP, OMAConstructed root, int spInstance)
273            throws IOException {
274        OMANode providerSubNode = root.addChild(getInstanceString(spInstance), null, null, null);
275
276        // The HomeSP:
277        OMANode homeSpNode = providerSubNode.addChild(TAG_HomeSP, null, null, null);
278        if (!homeSP.getSSIDs().isEmpty()) {
279            OMAConstructed nwkIDNode =
280                    (OMAConstructed) homeSpNode.addChild(TAG_NetworkID, null, null, null);
281            int instance = 0;
282            for (Map.Entry<String, Long> entry : homeSP.getSSIDs().entrySet()) {
283                OMAConstructed inode =
284                        (OMAConstructed) nwkIDNode.addChild(getInstanceString(instance++), null, null, null);
285                inode.addChild(TAG_SSID, null, entry.getKey(), null);
286                if (entry.getValue() != null) {
287                    inode.addChild(TAG_HESSID, null, String.format("%012x", entry.getValue()), null);
288                }
289            }
290        }
291
292        homeSpNode.addChild(TAG_FriendlyName, null, homeSP.getFriendlyName(), null);
293
294        if (homeSP.getIconURL() != null) {
295            homeSpNode.addChild(TAG_IconURL, null, homeSP.getIconURL(), null);
296        }
297
298        homeSpNode.addChild(TAG_FQDN, null, homeSP.getFQDN(), null);
299
300        if (!homeSP.getMatchAllOIs().isEmpty() || !homeSP.getMatchAnyOIs().isEmpty()) {
301            OMAConstructed homeOIList =
302                    (OMAConstructed) homeSpNode.addChild(TAG_HomeOIList, null, null, null);
303
304            int instance = 0;
305            for (Long oi : homeSP.getMatchAllOIs()) {
306                OMAConstructed inode =
307                        (OMAConstructed) homeOIList.addChild(getInstanceString(instance++),
308                                null, null, null);
309                inode.addChild(TAG_HomeOI, null, String.format("%x", oi), null);
310                inode.addChild(TAG_HomeOIRequired, null, "TRUE", null);
311            }
312            for (Long oi : homeSP.getMatchAnyOIs()) {
313                OMAConstructed inode =
314                        (OMAConstructed) homeOIList.addChild(getInstanceString(instance++),
315                                null, null, null);
316                inode.addChild(TAG_HomeOI, null, String.format("%x", oi), null);
317                inode.addChild(TAG_HomeOIRequired, null, "FALSE", null);
318            }
319        }
320
321        if (!homeSP.getOtherHomePartners().isEmpty()) {
322            OMAConstructed otherPartners =
323                    (OMAConstructed) homeSpNode.addChild(TAG_OtherHomePartners, null, null, null);
324            int instance = 0;
325            for (String fqdn : homeSP.getOtherHomePartners()) {
326                OMAConstructed inode =
327                        (OMAConstructed) otherPartners.addChild(getInstanceString(instance++),
328                                null, null, null);
329                inode.addChild(TAG_FQDN, null, fqdn, null);
330            }
331        }
332
333        if (!homeSP.getRoamingConsortiums().isEmpty()) {
334            homeSpNode.addChild(TAG_RoamingConsortiumOI, null, getRCList(homeSP.getRoamingConsortiums()), null);
335        }
336
337        // The Credential:
338        OMANode credentialNode = providerSubNode.addChild(TAG_Credential, null, null, null);
339        Credential cred = homeSP.getCredential();
340        EAPMethod method = cred.getEAPMethod();
341
342        if (cred.getCtime() > 0) {
343            credentialNode.addChild(TAG_CreationDate,
344                    null, DTFormat.format(new Date(cred.getCtime())), null);
345        }
346        if (cred.getExpTime() > 0) {
347            credentialNode.addChild(TAG_ExpirationDate,
348                    null, DTFormat.format(new Date(cred.getExpTime())), null);
349        }
350
351        if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_SIM
352                || method.getEAPMethodID() == EAP.EAPMethodID.EAP_AKA
353                || method.getEAPMethodID() == EAP.EAPMethodID.EAP_AKAPrim) {
354
355            OMANode simNode = credentialNode.addChild(TAG_SIM, null, null, null);
356            simNode.addChild(TAG_IMSI, null, cred.getImsi(), null);
357            simNode.addChild(TAG_EAPType, null,
358                    Integer.toString(EAP.mapEAPMethod(method.getEAPMethodID())), null);
359
360        } else if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_TTLS) {
361
362            OMANode unpNode = credentialNode.addChild(TAG_UsernamePassword, null, null, null);
363            unpNode.addChild(TAG_Username, null, cred.getUserName(), null);
364            unpNode.addChild(TAG_Password, null,
365                    Base64.encodeToString(cred.getPassword().getBytes(StandardCharsets.UTF_8),
366                            Base64.DEFAULT), null);
367            OMANode eapNode = unpNode.addChild(TAG_EAPMethod, null, null, null);
368            eapNode.addChild(TAG_EAPType, null,
369                    Integer.toString(EAP.mapEAPMethod(method.getEAPMethodID())), null);
370            eapNode.addChild(TAG_InnerMethod, null,
371                    ((NonEAPInnerAuth) method.getAuthParam()).getOMAtype(), null);
372
373        } else if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_TLS) {
374
375            OMANode certNode = credentialNode.addChild(TAG_DigitalCertificate, null, null, null);
376            certNode.addChild(TAG_CertificateType, null, Credential.CertTypeX509, null);
377            certNode.addChild(TAG_CertSHA256Fingerprint, null,
378                    Utils.toHex(cred.getFingerPrint()), null);
379
380        } else {
381            throw new OMAException("Invalid credential on " + homeSP.getFQDN());
382        }
383
384        credentialNode.addChild(TAG_Realm, null, cred.getRealm(), null);
385
386        // !!! Note: This node defines CRL checking through OSCP, I suspect we won't be able
387        // to do that so it is commented out:
388        //credentialNode.addChild(TAG_CheckAAAServerCertStatus, null, "TRUE", null);
389    }
390
391    private static String getInstanceString(int instance) {
392        return "i" + instance;
393    }
394
395    private static String getRCList(Collection<Long> rcs) {
396        StringBuilder builder = new StringBuilder();
397        boolean first = true;
398        for (Long roamingConsortium : rcs) {
399            if (first) {
400                first = false;
401            }
402            else {
403                builder.append(',');
404            }
405            builder.append(String.format("%x", roamingConsortium));
406        }
407        return builder.toString();
408    }
409
410    private static List<HomeSP> buildSPs(MOTree moTree) throws OMAException {
411        Log.d("HS2J", "MO root: " + moTree.getRoot().getName());
412        OMAConstructed spList;
413        if (moTree.getRoot().getName().equals(TAG_PerProviderSubscription)) {
414            // The PPS file is rooted at PPS instead of MgmtTree to conserve space
415            spList = moTree.getRoot();
416        }
417        else {
418            List<String> spPath = Arrays.asList(TAG_PerProviderSubscription);
419            spList = moTree.getRoot().getListValue(spPath.iterator());
420        }
421
422        List<HomeSP> homeSPs = new ArrayList<>();
423
424        if (spList == null) {
425            return homeSPs;
426        }
427        for (OMANode spRoot : spList.getChildren()) {
428            homeSPs.add(buildHomeSP(spRoot));
429        }
430
431        return homeSPs;
432    }
433
434    private static HomeSP buildHomeSP(OMANode ppsRoot) throws OMAException {
435        OMANode spRoot = ppsRoot.getChild(TAG_HomeSP);
436
437        String fqdn = spRoot.getScalarValue(Arrays.asList(TAG_FQDN).iterator());
438        String friendlyName = spRoot.getScalarValue(Arrays.asList(TAG_FriendlyName).iterator());
439        String iconURL = spRoot.getScalarValue(Arrays.asList(TAG_IconURL).iterator());
440
441        HashSet<Long> roamingConsortiums = new HashSet<>();
442        String oiString = spRoot.getScalarValue(Arrays.asList(TAG_RoamingConsortiumOI).iterator());
443        if (oiString != null) {
444            for (String oi : oiString.split(",")) {
445                roamingConsortiums.add(Long.parseLong(oi.trim(), 16));
446            }
447        }
448
449        Map<String, Long> ssids = new HashMap<>();
450
451        OMANode ssidListNode = spRoot.getListValue(Arrays.asList(TAG_NetworkID).iterator());
452        if (ssidListNode != null) {
453            for (OMANode ssidRoot : ssidListNode.getChildren()) {
454                OMANode hessidNode = ssidRoot.getChild(TAG_HESSID);
455                ssids.put(ssidRoot.getChild(TAG_SSID).getValue(), getMac(hessidNode));
456            }
457        }
458
459        Set<Long> matchAnyOIs = new HashSet<>();
460        List<Long> matchAllOIs = new ArrayList<>();
461        OMANode homeOIListNode = spRoot.getListValue(Arrays.asList(TAG_HomeOIList).iterator());
462        if (homeOIListNode != null) {
463            for (OMANode homeOIRoot : homeOIListNode.getChildren()) {
464                String homeOI = homeOIRoot.getChild(TAG_HomeOI).getValue();
465                if (Boolean.parseBoolean(homeOIRoot.getChild(TAG_HomeOIRequired).getValue())) {
466                    matchAllOIs.add(Long.parseLong(homeOI, 16));
467                } else {
468                    matchAnyOIs.add(Long.parseLong(homeOI, 16));
469                }
470            }
471        }
472
473        Set<String> otherHomePartners = new HashSet<>();
474        OMANode otherListNode =
475                spRoot.getListValue(Arrays.asList(TAG_OtherHomePartners).iterator());
476        if (otherListNode != null) {
477            for (OMANode fqdnNode : otherListNode.getChildren()) {
478                otherHomePartners.add(fqdnNode.getChild(TAG_FQDN).getValue());
479            }
480        }
481
482        Credential credential = buildCredential(ppsRoot.getChild(TAG_Credential));
483
484        return new HomeSP(ssids, fqdn, roamingConsortiums, otherHomePartners,
485                matchAnyOIs, matchAllOIs, friendlyName, iconURL, credential);
486    }
487
488    private static Credential buildCredential(OMANode credNode) throws OMAException {
489        long ctime = getTime(credNode.getChild(TAG_CreationDate));
490        long expTime = getTime(credNode.getChild(TAG_ExpirationDate));
491        String realm = getString(credNode.getChild(TAG_Realm));
492        boolean checkAAACert = getBoolean(credNode.getChild(TAG_CheckAAAServerCertStatus));
493
494        OMANode unNode = credNode.getChild(TAG_UsernamePassword);
495        OMANode certNode = credNode.getChild(TAG_DigitalCertificate);
496        OMANode simNode = credNode.getChild(TAG_SIM);
497
498        int alternatives = 0;
499        alternatives += unNode != null ? 1 : 0;
500        alternatives += certNode != null ? 1 : 0;
501        alternatives += simNode != null ? 1 : 0;
502        if (alternatives != 1) {
503            throw new OMAException("Expected exactly one credential type, got " + alternatives);
504        }
505
506        if (unNode != null) {
507            String userName = getString(unNode.getChild(TAG_Username));
508            String password = getString(unNode.getChild(TAG_Password));
509            boolean machineManaged = getBoolean(unNode.getChild(TAG_MachineManaged));
510            String softTokenApp = getString(unNode.getChild(TAG_SoftTokenApp));
511            boolean ableToShare = getBoolean(unNode.getChild(TAG_AbleToShare));
512
513            OMANode eapMethodNode = unNode.getChild(TAG_EAPMethod);
514            int eapID = getInteger(eapMethodNode.getChild(TAG_EAPType));
515
516            EAP.EAPMethodID eapMethodID = EAP.mapEAPMethod(eapID);
517            if (eapMethodID == null) {
518                throw new OMAException("Unknown EAP method: " + eapID);
519            }
520
521            Long vid = getOptionalInteger(eapMethodNode.getChild(TAG_VendorId));
522            Long vtype = getOptionalInteger(eapMethodNode.getChild(TAG_VendorType));
523            Long innerEAPType = getOptionalInteger(eapMethodNode.getChild(TAG_InnerEAPType));
524            EAP.EAPMethodID innerEAPMethod = null;
525            if (innerEAPType != null) {
526                innerEAPMethod = EAP.mapEAPMethod(innerEAPType.intValue());
527                if (innerEAPMethod == null) {
528                    throw new OMAException("Bad inner EAP method: " + innerEAPType);
529                }
530            }
531
532            Long innerVid = getOptionalInteger(eapMethodNode.getChild(TAG_InnerVendorID));
533            Long innerVtype = getOptionalInteger(eapMethodNode.getChild(TAG_InnerVendorType));
534            String innerNonEAPMethod = getString(eapMethodNode.getChild(TAG_InnerMethod));
535
536            EAPMethod eapMethod;
537            if (innerEAPMethod != null) {
538                eapMethod = new EAPMethod(eapMethodID, new InnerAuthEAP(innerEAPMethod));
539            } else if (vid != null) {
540                eapMethod = new EAPMethod(eapMethodID,
541                        new ExpandedEAPMethod(EAP.AuthInfoID.ExpandedEAPMethod,
542                                vid.intValue(), vtype));
543            } else if (innerVid != null) {
544                eapMethod =
545                        new EAPMethod(eapMethodID, new ExpandedEAPMethod(EAP.AuthInfoID
546                                .ExpandedInnerEAPMethod, innerVid.intValue(), innerVtype));
547            } else if (innerNonEAPMethod != null) {
548                eapMethod = new EAPMethod(eapMethodID, new NonEAPInnerAuth(innerNonEAPMethod));
549            } else {
550                throw new OMAException("Incomplete set of EAP parameters");
551            }
552
553            return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, userName,
554                    password, machineManaged, softTokenApp, ableToShare);
555        }
556        if (certNode != null) {
557            try {
558                String certTypeString = getString(certNode.getChild(TAG_CertificateType));
559                byte[] fingerPrint = getOctets(certNode.getChild(TAG_CertSHA256Fingerprint));
560
561                EAPMethod eapMethod = new EAPMethod(EAP.EAPMethodID.EAP_TLS, null);
562
563                return new Credential(ctime, expTime, realm, checkAAACert, eapMethod,
564                        Credential.mapCertType(certTypeString), fingerPrint);
565            }
566            catch (NumberFormatException nfe) {
567                throw new OMAException("Bad hex string: " + nfe.toString());
568            }
569        }
570        if (simNode != null) {
571
572            String imsi = getString(simNode.getChild(TAG_IMSI));
573            EAPMethod eapMethod =
574                    new EAPMethod(EAP.mapEAPMethod(getInteger(simNode.getChild(TAG_EAPType))),
575                            null);
576
577            return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, imsi);
578        }
579        throw new OMAException("Missing credential parameters");
580    }
581
582    private static boolean getBoolean(OMANode boolNode) {
583        return boolNode != null && Boolean.parseBoolean(boolNode.getValue());
584    }
585
586    private static String getString(OMANode stringNode) {
587        return stringNode != null ? stringNode.getValue() : null;
588    }
589
590    private static int getInteger(OMANode intNode) throws OMAException {
591        if (intNode == null) {
592            throw new OMAException("Missing integer value");
593        }
594        try {
595            return Integer.parseInt(intNode.getValue());
596        } catch (NumberFormatException nfe) {
597            throw new OMAException("Invalid integer: " + intNode.getValue());
598        }
599    }
600
601    private static Long getMac(OMANode macNode) throws OMAException {
602        if (macNode == null) {
603            return null;
604        }
605        try {
606            return Long.parseLong(macNode.getValue(), 16);
607        } catch (NumberFormatException nfe) {
608            throw new OMAException("Invalid MAC: " + macNode.getValue());
609        }
610    }
611
612    private static Long getOptionalInteger(OMANode intNode) throws OMAException {
613        if (intNode == null) {
614            return null;
615        }
616        try {
617            return Long.parseLong(intNode.getValue());
618        } catch (NumberFormatException nfe) {
619            throw new OMAException("Invalid integer: " + intNode.getValue());
620        }
621    }
622
623    private static long getTime(OMANode timeNode) throws OMAException {
624        if (timeNode == null) {
625            return -1;
626        }
627        String timeText = timeNode.getValue();
628        try {
629            Date date = DTFormat.parse(timeText);
630            return date.getTime();
631        } catch (ParseException pe) {
632            throw new OMAException("Badly formatted time: " + timeText);
633        }
634    }
635
636    private static byte[] getOctets(OMANode octetNode) throws OMAException {
637        if (octetNode == null) {
638            throw new OMAException("Missing byte value");
639        }
640        return Utils.hexToBytes(octetNode.getValue());
641    }
642}
643