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