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