MOManager.java revision e2434ec1d0c1c75709f55dcee5e8943c1d68e6c1
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 if (spList == null) { 254 return homeSPs; 255 } 256 257 for (OMANode spRoot : spList.getChildren()) { 258 homeSPs.add(buildHomeSP(spRoot)); 259 } 260 261 return homeSPs; 262 } 263 264 private static HomeSP buildHomeSP(OMANode ppsRoot) throws OMAException { 265 OMANode spRoot = ppsRoot.getChild(TAG_HomeSP); 266 267 String fqdn = spRoot.getScalarValue(Arrays.asList(TAG_FQDN).iterator()); 268 String friendlyName = spRoot.getScalarValue(Arrays.asList(TAG_FriendlyName).iterator()); 269 System.out.println("FQDN: " + fqdn + ", friendly: " + friendlyName); 270 String iconURL = spRoot.getScalarValue(Arrays.asList(TAG_IconURL).iterator()); 271 272 Set<Long> roamingConsortiums = new HashSet<Long>(); 273 String oiString = spRoot.getScalarValue(Arrays.asList(TAG_RoamingConsortiumOI).iterator()); 274 if (oiString != null) { 275 for (String oi : oiString.split(",")) { 276 roamingConsortiums.add(Long.parseLong(oi.trim(), 16)); 277 } 278 } 279 280 Map<String, Long> ssids = new HashMap<String, Long>(); 281 282 OMANode ssidListNode = spRoot.getListValue(Arrays.asList(TAG_NetworkID).iterator()); 283 if (ssidListNode != null) { 284 for (OMANode ssidRoot : ssidListNode.getChildren()) { 285 OMANode hessidNode = ssidRoot.getChild(TAG_HESSID); 286 ssids.put(ssidRoot.getChild(TAG_SSID).getValue(), getMac(hessidNode)); 287 } 288 } 289 290 Set<Long> matchAnyOIs = new HashSet<Long>(); 291 List<Long> matchAllOIs = new ArrayList<Long>(); 292 OMANode homeOIListNode = spRoot.getListValue(Arrays.asList(TAG_HomeOIList).iterator()); 293 if (homeOIListNode != null) { 294 for (OMANode homeOIRoot : homeOIListNode.getChildren()) { 295 String homeOI = homeOIRoot.getChild(TAG_HomeOI).getValue(); 296 if (Boolean.parseBoolean(homeOIRoot.getChild(TAG_HomeOIRequired).getValue())) { 297 matchAllOIs.add(Long.parseLong(homeOI, 16)); 298 } else { 299 matchAnyOIs.add(Long.parseLong(homeOI, 16)); 300 } 301 } 302 } 303 304 Set<String> otherHomePartners = new HashSet<String>(); 305 OMANode otherListNode = 306 spRoot.getListValue(Arrays.asList(TAG_OtherHomePartners).iterator()); 307 if (otherListNode != null) { 308 for (OMANode fqdnNode : otherListNode.getChildren()) { 309 otherHomePartners.add(fqdnNode.getChild(TAG_FQDN).getValue()); 310 } 311 } 312 313 Credential credential = buildCredential(ppsRoot.getChild(TAG_Credential)); 314 315 return new HomeSP(ssids, fqdn, roamingConsortiums, otherHomePartners, 316 matchAnyOIs, matchAllOIs, friendlyName, iconURL, credential); 317 } 318 319 private static Credential buildCredential(OMANode credNode) throws OMAException { 320 long ctime = getTime(credNode.getChild(TAG_CreationDate)); 321 long expTime = getTime(credNode.getChild(TAG_ExpirationDate)); 322 String realm = getString(credNode.getChild(TAG_Realm)); 323 boolean checkAAACert = getBoolean(credNode.getChild(TAG_CheckAAAServerCertStatus)); 324 325 OMANode unNode = credNode.getChild(TAG_UsernamePassword); 326 OMANode certNode = credNode.getChild(TAG_DigitalCertificate); 327 OMANode simNode = credNode.getChild(TAG_SIM); 328 329 int alternatives = 0; 330 alternatives += unNode != null ? 1 : 0; 331 alternatives += certNode != null ? 1 : 0; 332 alternatives += simNode != null ? 1 : 0; 333 if (alternatives != 1) { 334 throw new OMAException("Expected exactly one credential type, got " + alternatives); 335 } 336 337 if (unNode != null) { 338 String userName = unNode.getChild(TAG_Username).getValue(); 339 String password = unNode.getChild(TAG_Password).getValue(); 340 boolean machineManaged = getBoolean(unNode.getChild(TAG_MachineManaged)); 341 String softTokenApp = getString(unNode.getChild(TAG_SoftTokenApp)); 342 boolean ableToShare = getBoolean(unNode.getChild(TAG_AbleToShare)); 343 344 OMANode eapMethodNode = unNode.getChild(TAG_EAPMethod); 345 EAP.EAPMethodID eapMethodID = 346 EAP.mapEAPMethod(getInteger(eapMethodNode.getChild(TAG_EAPType))); 347 if (eapMethodID == null) { 348 throw new OMAException("Unknown EAP method"); 349 } 350 351 Long vid = getOptionalInteger(eapMethodNode.getChild(TAG_VendorId)); 352 Long vtype = getOptionalInteger(eapMethodNode.getChild(TAG_VendorType)); 353 Long innerEAPType = getOptionalInteger(eapMethodNode.getChild(TAG_InnerEAPType)); 354 EAP.EAPMethodID innerEAPMethod = null; 355 if (innerEAPType != null) { 356 innerEAPMethod = EAP.mapEAPMethod(innerEAPType.intValue()); 357 if (innerEAPMethod == null) { 358 throw new OMAException("Bad inner EAP method: " + innerEAPType); 359 } 360 } 361 362 Long innerVid = getOptionalInteger(eapMethodNode.getChild(TAG_InnerVendorID)); 363 Long innerVtype = getOptionalInteger(eapMethodNode.getChild(TAG_InnerVendorType)); 364 String innerNonEAPMethod = getString(eapMethodNode.getChild(TAG_InnerMethod)); 365 366 EAPMethod eapMethod; 367 if (innerEAPMethod != null) { 368 eapMethod = new EAPMethod(eapMethodID, new InnerAuthEAP(innerEAPMethod)); 369 } else if (vid != null) { 370 eapMethod = new EAPMethod(eapMethodID, 371 new ExpandedEAPMethod(EAP.AuthInfoID.ExpandedEAPMethod, 372 vid.intValue(), vtype)); 373 } else if (innerVid != null) { 374 eapMethod = 375 new EAPMethod(eapMethodID, new ExpandedEAPMethod(EAP.AuthInfoID 376 .ExpandedInnerEAPMethod, innerVid.intValue(), innerVtype)); 377 } else if (innerNonEAPMethod != null) { 378 eapMethod = new EAPMethod(eapMethodID, new NonEAPInnerAuth(innerNonEAPMethod)); 379 } else { 380 throw new OMAException("Incomplete set of EAP parameters"); 381 } 382 383 return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, userName, 384 password, machineManaged, softTokenApp, ableToShare); 385 } 386 if (certNode != null) { 387 try { 388 String certTypeString = getString(certNode.getChild(TAG_CertificateType)); 389 byte[] fingerPrint = getOctets(certNode.getChild(TAG_CertSHA256Fingerprint)); 390 391 EAPMethod eapMethod = new EAPMethod(EAP.EAPMethodID.EAP_TLS, null); 392 393 return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, 394 Credential.mapCertType(certTypeString), fingerPrint); 395 } 396 catch (NumberFormatException nfe) { 397 throw new OMAException("Bad hex string: " + nfe.toString()); 398 } 399 } 400 if (simNode != null) { 401 402 String imsi = getString(simNode.getChild(TAG_IMSI)); 403 EAPMethod eapMethod = 404 new EAPMethod(EAP.mapEAPMethod(getInteger(simNode.getChild(TAG_EAPType))), 405 null); 406 407 return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, imsi); 408 } 409 throw new OMAException("Missing credential parameters"); 410 } 411 412 private static boolean getBoolean(OMANode boolNode) { 413 return boolNode != null && Boolean.parseBoolean(boolNode.getValue()); 414 } 415 416 private static String getString(OMANode stringNode) { 417 return stringNode != null ? stringNode.getValue() : null; 418 } 419 420 private static int getInteger(OMANode intNode) throws OMAException { 421 if (intNode == null) { 422 throw new OMAException("Missing integer value"); 423 } 424 try { 425 return Integer.parseInt(intNode.getValue()); 426 } catch (NumberFormatException nfe) { 427 throw new OMAException("Invalid integer: " + intNode.getValue()); 428 } 429 } 430 431 private static Long getMac(OMANode macNode) throws OMAException { 432 if (macNode == null) { 433 return null; 434 } 435 try { 436 return Long.parseLong(macNode.getValue(), 16); 437 } catch (NumberFormatException nfe) { 438 throw new OMAException("Invalid MAC: " + macNode.getValue()); 439 } 440 } 441 442 private static Long getOptionalInteger(OMANode intNode) throws OMAException { 443 if (intNode == null) { 444 return null; 445 } 446 try { 447 return Long.parseLong(intNode.getValue()); 448 } catch (NumberFormatException nfe) { 449 throw new OMAException("Invalid integer: " + intNode.getValue()); 450 } 451 } 452 453 private static long getTime(OMANode timeNode) throws OMAException { 454 if (timeNode == null) { 455 return -1; 456 } 457 String timeText = timeNode.getValue(); 458 try { 459 Date date = DTFormat.parse(timeText); 460 return date.getTime(); 461 } catch (ParseException pe) { 462 throw new OMAException("Badly formatted time: " + timeText); 463 } 464 } 465 466 private static byte[] getOctets(OMANode octetNode) throws OMAException { 467 if (octetNode == null) { 468 throw new OMAException("Missing byte value"); 469 } 470 return Utils.hexToBytes(octetNode.getValue()); 471 } 472} 473