MOManager.java revision 71a988c8e9859244b83cd55bb6b6ee913fcaf95c
1package com.android.server.wifi.hotspot2.omadm; 2 3import com.android.server.wifi.anqp.eap.EAP; 4import com.android.server.wifi.anqp.eap.EAPMethod; 5import com.android.server.wifi.anqp.eap.ExpandedEAPMethod; 6import com.android.server.wifi.anqp.eap.InnerAuthEAP; 7import com.android.server.wifi.anqp.eap.NonEAPInnerAuth; 8import com.android.server.wifi.hotspot2.pps.Credential; 9import com.android.server.wifi.hotspot2.pps.HomeSP; 10 11import org.xml.sax.SAXException; 12 13import java.io.BufferedInputStream; 14import java.io.BufferedOutputStream; 15import java.io.BufferedReader; 16import java.io.File; 17import java.io.FileInputStream; 18import java.io.FileOutputStream; 19import java.io.FileReader; 20import java.io.IOException; 21import java.io.InputStream; 22import java.text.DateFormat; 23import java.text.ParseException; 24import java.text.SimpleDateFormat; 25import java.util.ArrayList; 26import java.util.Arrays; 27import java.util.Date; 28import java.util.HashMap; 29import java.util.HashSet; 30import java.util.List; 31import java.util.Map; 32import java.util.Set; 33import java.util.TimeZone; 34 35/** 36 * Handles provisioning of PerProviderSubscription data. 37 */ 38public class MOManager { 39 private final File mPpsFile; 40 private final Map<String, HomeSP> mSPs; 41 42 public MOManager(File ppsFile) throws IOException { 43 mPpsFile = ppsFile; 44 mSPs = new HashMap<String, HomeSP>(); 45 } 46 47 public File getPpsFile() { 48 return mPpsFile; 49 } 50 51 public List<HomeSP> loadAllSPs() throws IOException { 52 List<MOTree> trees = new ArrayList<MOTree>(); 53 List<HomeSP> sps = new ArrayList<HomeSP>(); 54 55 if (!mPpsFile.exists()) { 56 return sps; 57 } 58 59 BufferedInputStream in = null; 60 try { 61 in = new BufferedInputStream(new FileInputStream(mPpsFile)); 62 while (in.available() > 0) { 63 MOTree tree = MOTree.unmarshal(in); 64 if (tree != null) { 65 trees.add(tree); 66 } else { 67 break; 68 } 69 } 70 } finally { 71 if (in != null) { 72 try { 73 in.close(); 74 } catch (IOException ioe) { 75 /**/ 76 } 77 } 78 } 79 80 for (MOTree moTree : trees) { 81 List<HomeSP> sp = buildSPs(moTree); 82 if (sp != null) { 83 sps.addAll(sp); 84 } 85 } 86 87 for (HomeSP sp : sps) { 88 if (mSPs.put(sp.getFQDN(), sp) != null) { 89 throw new OMAException("Multiple SPs for FQDN '" + sp.getFQDN() + "'"); 90 } 91 } 92 return sps; 93 } 94 95 public HomeSP addSP(InputStream xmlIn) throws IOException, SAXException { 96 OMAParser omaParser = new OMAParser(); 97 MOTree tree = omaParser.parse(xmlIn, OMAConstants.LOC_PPS + ":1.0"); 98 List<HomeSP> spList = buildSPs(tree); 99 if (spList.size() != 1) { 100 throw new OMAException("Expected exactly one HomeSP, got " + spList.size()); 101 } 102 HomeSP sp = spList.iterator().next(); 103 String fqdn = sp.getFQDN(); 104 if (mSPs.put(fqdn, sp) != null) { 105 throw new OMAException("SP " + fqdn + " already exists"); 106 } 107 108 BufferedOutputStream out = null; 109 try { 110 out = new BufferedOutputStream(new FileOutputStream(mPpsFile, true)); 111 tree.marshal(out); 112 out.flush(); 113 } finally { 114 if (out != null) { 115 try { 116 out.close(); 117 } catch (IOException ioe) { 118 /**/ 119 } 120 } 121 } 122 123 return sp; 124 } 125 126 private static final DateFormat DTFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); 127 128 static { 129 DTFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 130 } 131 132 public static final String TAG_AAAServerTrustRoot = "AAAServerTrustRoot"; 133 public static final String TAG_AbleToShare = "AbleToShare"; 134 public static final String TAG_CertificateType = "CertificateType"; 135 public static final String TAG_CertSHA256Fingerprint = "CertSHA256Fingerprint"; 136 public static final String TAG_CertURL = "CertURL"; 137 public static final String TAG_CheckAAAServerCertStatus = "CheckAAAServerCertStatus"; 138 public static final String TAG_Country = "Country"; 139 public static final String TAG_CreationDate = "CreationDate"; 140 public static final String TAG_Credential = "Credential"; 141 public static final String TAG_CredentialPriority = "CredentialPriority"; 142 public static final String TAG_DataLimit = "DataLimit"; 143 public static final String TAG_DigitalCertificate = "DigitalCertificate"; 144 public static final String TAG_DLBandwidth = "DLBandwidth"; 145 public static final String TAG_EAPMethod = "EAPMethod"; 146 public static final String TAG_EAPType = "EAPType"; 147 public static final String TAG_ExpirationDate = "ExpirationDate"; 148 public static final String TAG_Extension = "Extension"; 149 public static final String TAG_FQDN = "FQDN"; 150 public static final String TAG_FQDN_Match = "FQDN_Match"; 151 public static final String TAG_FriendlyName = "FriendlyName"; 152 public static final String TAG_HESSID = "HESSID"; 153 public static final String TAG_HomeOI = "HomeOI"; 154 public static final String TAG_HomeOIList = "HomeOIList"; 155 public static final String TAG_HomeOIRequired = "HomeOIRequired"; 156 public static final String TAG_HomeSP = "HomeSP"; 157 public static final String TAG_IconURL = "IconURL"; 158 public static final String TAG_IMSI = "IMSI"; 159 public static final String TAG_InnerEAPType = "InnerEAPType"; 160 public static final String TAG_InnerMethod = "InnerMethod"; 161 public static final String TAG_InnerVendorID = "InnerVendorID"; 162 public static final String TAG_InnerVendorType = "InnerVendorType"; 163 public static final String TAG_IPProtocol = "IPProtocol"; 164 public static final String TAG_MachineManaged = "MachineManaged"; 165 public static final String TAG_MaximumBSSLoadValue = "MaximumBSSLoadValue"; 166 public static final String TAG_MinBackhaulThreshold = "MinBackhaulThreshold"; 167 public static final String TAG_NetworkID = "NetworkID"; 168 public static final String TAG_NetworkType = "NetworkType"; 169 public static final String TAG_Other = "Other"; 170 public static final String TAG_OtherHomePartners = "OtherHomePartners"; 171 public static final String TAG_Password = "Password"; 172 public static final String TAG_PerProviderSubscription = "PerProviderSubscription"; 173 public static final String TAG_Policy = "Policy"; 174 public static final String TAG_PolicyUpdate = "PolicyUpdate"; 175 public static final String TAG_PortNumber = "PortNumber"; 176 public static final String TAG_PreferredRoamingPartnerList = "PreferredRoamingPartnerList"; 177 public static final String TAG_Priority = "Priority"; 178 public static final String TAG_Realm = "Realm"; 179 public static final String TAG_RequiredProtoPortTuple = "RequiredProtoPortTuple"; 180 public static final String TAG_Restriction = "Restriction"; 181 public static final String TAG_RoamingConsortiumOI = "RoamingConsortiumOI"; 182 public static final String TAG_SIM = "SIM"; 183 public static final String TAG_SoftTokenApp = "SoftTokenApp"; 184 public static final String TAG_SPExclusionList = "SPExclusionList"; 185 public static final String TAG_SSID = "SSID"; 186 public static final String TAG_StartDate = "StartDate"; 187 public static final String TAG_SubscriptionParameters = "SubscriptionParameters"; 188 public static final String TAG_SubscriptionUpdate = "SubscriptionUpdate"; 189 public static final String TAG_TimeLimit = "TimeLimit"; 190 public static final String TAG_TrustRoot = "TrustRoot"; 191 public static final String TAG_TypeOfSubscription = "TypeOfSubscription"; 192 public static final String TAG_ULBandwidth = "ULBandwidth"; 193 public static final String TAG_UpdateIdentifier = "UpdateIdentifier"; 194 public static final String TAG_UpdateInterval = "UpdateInterval"; 195 public static final String TAG_UpdateMethod = "UpdateMethod"; 196 public static final String TAG_URI = "URI"; 197 public static final String TAG_UsageLimits = "UsageLimits"; 198 public static final String TAG_UsageTimePeriod = "UsageTimePeriod"; 199 public static final String TAG_Username = "Username"; 200 public static final String TAG_UsernamePassword = "UsernamePassword"; 201 public static final String TAG_VendorId = "VendorId"; 202 public static final String TAG_VendorType = "VendorType"; 203 204 private static List<HomeSP> buildSPs(MOTree moTree) throws OMAException { 205 List<String> spPath = Arrays.asList(TAG_PerProviderSubscription); 206 OMAConstructed spList = moTree.getRoot().getListValue(spPath.iterator()); 207 208 List<HomeSP> homeSPs = new ArrayList<HomeSP>(); 209 210 for (OMANode spRoot : spList.getChildren()) { 211 homeSPs.add(buildHomeSP(spRoot)); 212 } 213 214 return homeSPs; 215 } 216 217 private static HomeSP buildHomeSP(OMANode ppsRoot) throws OMAException { 218 OMANode spRoot = ppsRoot.getChild(TAG_HomeSP); 219 220 String fqdn = spRoot.getScalarValue(Arrays.asList(TAG_FQDN).iterator()); 221 String friendlyName = spRoot.getScalarValue(Arrays.asList(TAG_FriendlyName).iterator()); 222 System.out.println("FQDN: " + fqdn + ", friendly: " + friendlyName); 223 String iconURL = spRoot.getScalarValue(Arrays.asList(TAG_IconURL).iterator()); 224 225 Set<Long> roamingConsortiums = new HashSet<Long>(); 226 String oiString = spRoot.getScalarValue(Arrays.asList(TAG_RoamingConsortiumOI).iterator()); 227 if (oiString != null) { 228 for (String oi : oiString.split(",")) { 229 roamingConsortiums.add(Long.parseLong(oi.trim(), 16)); 230 } 231 } 232 233 Map<String, String> ssids = new HashMap<String, String>(); 234 235 OMANode ssidListNode = spRoot.getListValue(Arrays.asList(TAG_NetworkID).iterator()); 236 if (ssidListNode != null) { 237 for (OMANode ssidRoot : ssidListNode.getChildren()) { 238 OMANode hessidNode = ssidRoot.getChild(TAG_HESSID); 239 ssids.put(ssidRoot.getChild(TAG_SSID).getValue(), 240 hessidNode != null ? hessidNode.getValue() : null); 241 } 242 } 243 244 Set<Long> matchAnyOIs = new HashSet<Long>(); 245 List<Long> matchAllOIs = new ArrayList<Long>(); 246 OMANode homeOIListNode = spRoot.getListValue(Arrays.asList(TAG_HomeOIList).iterator()); 247 if (homeOIListNode != null) { 248 for (OMANode homeOIRoot : homeOIListNode.getChildren()) { 249 String homeOI = homeOIRoot.getChild(TAG_HomeOI).getValue(); 250 if (Boolean.parseBoolean(homeOIRoot.getChild(TAG_HomeOIRequired).getValue())) { 251 matchAllOIs.add(Long.parseLong(homeOI, 16)); 252 } else { 253 matchAnyOIs.add(Long.parseLong(homeOI, 16)); 254 } 255 } 256 } 257 258 Set<String> otherHomePartners = new HashSet<String>(); 259 OMANode otherListNode = 260 spRoot.getListValue(Arrays.asList(TAG_OtherHomePartners).iterator()); 261 if (otherListNode != null) { 262 for (OMANode fqdnNode : otherListNode.getChildren()) { 263 otherHomePartners.add(fqdnNode.getChild(TAG_FQDN).getValue()); 264 } 265 } 266 267 Credential credential = buildCredential(ppsRoot.getChild(TAG_Credential)); 268 269 return new HomeSP(ssids, fqdn, roamingConsortiums, otherHomePartners, 270 matchAnyOIs, matchAllOIs, friendlyName, iconURL, credential); 271 } 272 273 private static Credential buildCredential(OMANode credNode) throws OMAException { 274 long ctime = getTime(credNode.getChild(TAG_CreationDate)); 275 long expTime = getTime(credNode.getChild(TAG_ExpirationDate)); 276 String realm = getString(credNode.getChild(TAG_Realm)); 277 boolean checkAAACert = getBoolean(credNode.getChild(TAG_CheckAAAServerCertStatus)); 278 279 OMANode unNode = credNode.getChild(TAG_UsernamePassword); 280 OMANode certNode = credNode.getChild(TAG_DigitalCertificate); 281 OMANode simNode = credNode.getChild(TAG_SIM); 282 283 int alternatives = 0; 284 alternatives += unNode != null ? 1 : 0; 285 alternatives += certNode != null ? 1 : 0; 286 alternatives += simNode != null ? 1 : 0; 287 if (alternatives != 1) { 288 throw new OMAException("Expected exactly one credential type, got " + alternatives); 289 } 290 291 if (unNode != null) { 292 String userName = unNode.getChild(TAG_Username).getValue(); 293 String password = unNode.getChild(TAG_Password).getValue(); 294 boolean machineManaged = getBoolean(unNode.getChild(TAG_MachineManaged)); 295 String softTokenApp = getString(unNode.getChild(TAG_SoftTokenApp)); 296 boolean ableToShare = getBoolean(unNode.getChild(TAG_AbleToShare)); 297 298 OMANode eapMethodNode = unNode.getChild(TAG_EAPMethod); 299 EAP.EAPMethodID eapMethodID = 300 EAP.mapEAPMethod(getInteger(eapMethodNode.getChild(TAG_EAPType))); 301 if (eapMethodID == null) { 302 throw new OMAException("Unknown EAP method"); 303 } 304 305 Long vid = getOptionalInteger(eapMethodNode.getChild(TAG_VendorId)); 306 Long vtype = getOptionalInteger(eapMethodNode.getChild(TAG_VendorType)); 307 Long innerEAPType = getOptionalInteger(eapMethodNode.getChild(TAG_InnerEAPType)); 308 EAP.EAPMethodID innerEAPMethod = null; 309 if (innerEAPType != null) { 310 innerEAPMethod = EAP.mapEAPMethod(innerEAPType.intValue()); 311 if (innerEAPMethod == null) { 312 throw new OMAException("Bad inner EAP method: " + innerEAPType); 313 } 314 } 315 316 Long innerVid = getOptionalInteger(eapMethodNode.getChild(TAG_InnerVendorID)); 317 Long innerVtype = getOptionalInteger(eapMethodNode.getChild(TAG_InnerVendorType)); 318 String innerNonEAPMethod = getString(eapMethodNode.getChild(TAG_InnerMethod)); 319 320 EAPMethod eapMethod; 321 if (innerEAPMethod != null) { 322 eapMethod = new EAPMethod(eapMethodID, new InnerAuthEAP(innerEAPMethod)); 323 } else if (vid != null) { 324 eapMethod = new EAPMethod(eapMethodID, 325 new ExpandedEAPMethod(EAP.AuthInfoID.ExpandedEAPMethod, 326 vid.intValue(), vtype)); 327 } else if (innerVid != null) { 328 eapMethod = 329 new EAPMethod(eapMethodID, new ExpandedEAPMethod(EAP.AuthInfoID 330 .ExpandedInnerEAPMethod, innerVid.intValue(), innerVtype)); 331 } else if (innerNonEAPMethod != null) { 332 eapMethod = new EAPMethod(eapMethodID, new NonEAPInnerAuth(innerNonEAPMethod)); 333 } else { 334 throw new OMAException("Incomplete set of EAP parameters"); 335 } 336 337 return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, userName, 338 password, machineManaged, softTokenApp, ableToShare); 339 } 340 if (certNode != null) { 341 String certTypeString = getString(certNode.getChild(TAG_CertificateType)); 342 byte[] fingerPrint = getOctets(certNode.getChild(TAG_CertSHA256Fingerprint)); 343 344 EAPMethod eapMethod = new EAPMethod(EAP.EAPMethodID.EAP_TLS, null); 345 346 return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, 347 Credential.mapCertType(certTypeString), fingerPrint); 348 } 349 if (simNode != null) { 350 351 String imsi = getString(simNode.getChild(TAG_IMSI)); 352 EAPMethod eapMethod = 353 new EAPMethod(EAP.mapEAPMethod(getInteger(simNode.getChild(TAG_EAPType))), 354 null); 355 356 return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, imsi); 357 } 358 throw new OMAException("Missing credential parameters"); 359 } 360 361 private static boolean getBoolean(OMANode boolNode) { 362 return boolNode != null && Boolean.parseBoolean(boolNode.getValue()); 363 } 364 365 private static String getString(OMANode stringNode) { 366 return stringNode != null ? stringNode.getValue() : null; 367 } 368 369 private static int getInteger(OMANode intNode) throws OMAException { 370 if (intNode == null) { 371 throw new OMAException("Missing integer value"); 372 } 373 try { 374 return Integer.parseInt(intNode.getValue()); 375 } catch (NumberFormatException nfe) { 376 throw new OMAException("Invalid integer: " + intNode.getValue()); 377 } 378 } 379 380 private static Long getOptionalInteger(OMANode intNode) throws OMAException { 381 if (intNode == null) { 382 return null; 383 } 384 try { 385 return Long.parseLong(intNode.getValue()); 386 } catch (NumberFormatException nfe) { 387 throw new OMAException("Invalid integer: " + intNode.getValue()); 388 } 389 } 390 391 private static long getTime(OMANode timeNode) throws OMAException { 392 if (timeNode == null) { 393 return -1; 394 } 395 String timeText = timeNode.getValue(); 396 try { 397 Date date = DTFormat.parse(timeText); 398 return date.getTime(); 399 } catch (ParseException pe) { 400 throw new OMAException("Badly formatted time: " + timeText); 401 } 402 } 403 404 private static byte[] getOctets(OMANode octetNode) throws OMAException { 405 if (octetNode == null) { 406 throw new OMAException("Missing byte value"); 407 } 408 String text = octetNode.getValue(); 409 if ((text.length() & 1) == 1) { 410 throw new OMAException("Odd length octet value: " + text); 411 } 412 byte[] octets = new byte[text.length() / 2]; 413 for (int n = 0; n < octets.length; n++) { 414 octets[n] = (byte) (fromHex(text.charAt(n * 2)) << Byte.SIZE | 415 fromHex(text.charAt(n * 2 + 1))); 416 } 417 return octets; 418 } 419 420 private static int fromHex(char ch) throws OMAException { 421 if (ch <= '9' && ch >= '0') { 422 return ch - '0'; 423 } else if (ch >= 'a' && ch <= 'f') { 424 return ch + 10 - 'a'; 425 } else if (ch <= 'F' && ch >= 'A') { 426 return ch + 10 - 'A'; 427 } else { 428 throw new OMAException("Bad hex-character: " + ch); 429 } 430 } 431 432} 433