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