1 /** 2 * Copyright (c) 2016, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.wifi.hotspot2; 18 19 import android.net.wifi.hotspot2.pps.Credential; 20 import android.net.wifi.hotspot2.pps.HomeSp; 21 import android.net.wifi.hotspot2.pps.Policy; 22 import android.net.wifi.hotspot2.pps.UpdateParameter; 23 import android.os.Parcelable; 24 import android.text.TextUtils; 25 import android.util.Log; 26 import android.os.Parcel; 27 28 import java.nio.charset.StandardCharsets; 29 import java.util.Arrays; 30 import java.util.Collections; 31 import java.util.Date; 32 import java.util.HashMap; 33 import java.util.Map; 34 import java.util.Objects; 35 36 /** 37 * Class representing Passpoint configuration. This contains configurations specified in 38 * PerProviderSubscription (PPS) Management Object (MO) tree. 39 * 40 * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0 41 * Release 2 Technical Specification. 42 */ 43 public final class PasspointConfiguration implements Parcelable { 44 private static final String TAG = "PasspointConfiguration"; 45 46 /** 47 * Number of bytes for certificate SHA-256 fingerprint byte array. 48 */ 49 private static final int CERTIFICATE_SHA256_BYTES = 32; 50 51 /** 52 * Maximum bytes for URL string. 53 */ 54 private static final int MAX_URL_BYTES = 1023; 55 56 /** 57 * Integer value used for indicating null value in the Parcel. 58 */ 59 private static final int NULL_VALUE = -1; 60 61 /** 62 * Configurations under HomeSp subtree. 63 */ 64 private HomeSp mHomeSp = null; 65 /** 66 * Set the Home SP (Service Provider) information. 67 * 68 * @param homeSp The Home SP information to set to 69 */ setHomeSp(HomeSp homeSp)70 public void setHomeSp(HomeSp homeSp) { mHomeSp = homeSp; } 71 /** 72 * Get the Home SP (Service Provider) information. 73 * 74 * @return Home SP information 75 */ getHomeSp()76 public HomeSp getHomeSp() { return mHomeSp; } 77 78 /** 79 * Configurations under Credential subtree. 80 */ 81 private Credential mCredential = null; 82 /** 83 * Set the credential information. 84 * 85 * @param credential The credential information to set to 86 */ setCredential(Credential credential)87 public void setCredential(Credential credential) { 88 mCredential = credential; 89 } 90 /** 91 * Get the credential information. 92 * 93 * @return credential information 94 */ getCredential()95 public Credential getCredential() { 96 return mCredential; 97 } 98 99 /** 100 * Configurations under Policy subtree. 101 */ 102 private Policy mPolicy = null; 103 /** 104 * @hide 105 */ setPolicy(Policy policy)106 public void setPolicy(Policy policy) { 107 mPolicy = policy; 108 } 109 /** 110 * @hide 111 */ getPolicy()112 public Policy getPolicy() { 113 return mPolicy; 114 } 115 116 /** 117 * Meta data for performing subscription update. 118 */ 119 private UpdateParameter mSubscriptionUpdate = null; 120 /** 121 * @hide 122 */ setSubscriptionUpdate(UpdateParameter subscriptionUpdate)123 public void setSubscriptionUpdate(UpdateParameter subscriptionUpdate) { 124 mSubscriptionUpdate = subscriptionUpdate; 125 } 126 /** 127 * @hide 128 */ getSubscriptionUpdate()129 public UpdateParameter getSubscriptionUpdate() { 130 return mSubscriptionUpdate; 131 } 132 133 /** 134 * List of HTTPS URL for retrieving trust root certificate and the corresponding SHA-256 135 * fingerprint of the certificate. The certificates are used for verifying AAA server's 136 * identity during EAP authentication. 137 */ 138 private Map<String, byte[]> mTrustRootCertList = null; 139 /** 140 * @hide 141 */ setTrustRootCertList(Map<String, byte[]> trustRootCertList)142 public void setTrustRootCertList(Map<String, byte[]> trustRootCertList) { 143 mTrustRootCertList = trustRootCertList; 144 } 145 /** 146 * @hide 147 */ getTrustRootCertList()148 public Map<String, byte[]> getTrustRootCertList() { 149 return mTrustRootCertList; 150 } 151 152 /** 153 * Set by the subscription server, updated every time the configuration is updated by 154 * the subscription server. 155 * 156 * Use Integer.MIN_VALUE to indicate unset value. 157 */ 158 private int mUpdateIdentifier = Integer.MIN_VALUE; 159 /** 160 * @hide 161 */ setUpdateIdentifier(int updateIdentifier)162 public void setUpdateIdentifier(int updateIdentifier) { 163 mUpdateIdentifier = updateIdentifier; 164 } 165 /** 166 * @hide 167 */ getUpdateIdentifier()168 public int getUpdateIdentifier() { 169 return mUpdateIdentifier; 170 } 171 172 /** 173 * The priority of the credential. 174 * 175 * Use Integer.MIN_VALUE to indicate unset value. 176 */ 177 private int mCredentialPriority = Integer.MIN_VALUE; 178 /** 179 * @hide 180 */ setCredentialPriority(int credentialPriority)181 public void setCredentialPriority(int credentialPriority) { 182 mCredentialPriority = credentialPriority; 183 } 184 /** 185 * @hide 186 */ getCredentialPriority()187 public int getCredentialPriority() { 188 return mCredentialPriority; 189 } 190 191 /** 192 * The time this subscription is created. It is in the format of number 193 * of milliseconds since January 1, 1970, 00:00:00 GMT. 194 * 195 * Use Long.MIN_VALUE to indicate unset value. 196 */ 197 private long mSubscriptionCreationTimeInMillis = Long.MIN_VALUE; 198 /** 199 * @hide 200 */ setSubscriptionCreationTimeInMillis(long subscriptionCreationTimeInMillis)201 public void setSubscriptionCreationTimeInMillis(long subscriptionCreationTimeInMillis) { 202 mSubscriptionCreationTimeInMillis = subscriptionCreationTimeInMillis; 203 } 204 /** 205 * @hide 206 */ getSubscriptionCreationTimeInMillis()207 public long getSubscriptionCreationTimeInMillis() { 208 return mSubscriptionCreationTimeInMillis; 209 } 210 211 /** 212 * The time this subscription will expire. It is in the format of number 213 * of milliseconds since January 1, 1970, 00:00:00 GMT. 214 * 215 * Use Long.MIN_VALUE to indicate unset value. 216 */ 217 private long mSubscriptionExpirationTimeInMillis = Long.MIN_VALUE; 218 /** 219 * @hide 220 */ setSubscriptionExpirationTimeInMillis(long subscriptionExpirationTimeInMillis)221 public void setSubscriptionExpirationTimeInMillis(long subscriptionExpirationTimeInMillis) { 222 mSubscriptionExpirationTimeInMillis = subscriptionExpirationTimeInMillis; 223 } 224 /** 225 * @hide 226 */ getSubscriptionExpirationTimeInMillis()227 public long getSubscriptionExpirationTimeInMillis() { 228 return mSubscriptionExpirationTimeInMillis; 229 } 230 231 /** 232 * The type of the subscription. This is defined by the provider and the value is provider 233 * specific. 234 */ 235 private String mSubscriptionType = null; 236 /** 237 * @hide 238 */ setSubscriptionType(String subscriptionType)239 public void setSubscriptionType(String subscriptionType) { 240 mSubscriptionType = subscriptionType; 241 } 242 /** 243 * @hide 244 */ getSubscriptionType()245 public String getSubscriptionType() { 246 return mSubscriptionType; 247 } 248 249 /** 250 * The time period for usage statistics accumulation. A value of zero means that usage 251 * statistics are not accumulated on a periodic basis (e.g., a one-time limit for 252 * “pay as you go” - PAYG service). A non-zero value specifies the usage interval in minutes. 253 */ 254 private long mUsageLimitUsageTimePeriodInMinutes = Long.MIN_VALUE; 255 /** 256 * @hide 257 */ setUsageLimitUsageTimePeriodInMinutes(long usageLimitUsageTimePeriodInMinutes)258 public void setUsageLimitUsageTimePeriodInMinutes(long usageLimitUsageTimePeriodInMinutes) { 259 mUsageLimitUsageTimePeriodInMinutes = usageLimitUsageTimePeriodInMinutes; 260 } 261 /** 262 * @hide 263 */ getUsageLimitUsageTimePeriodInMinutes()264 public long getUsageLimitUsageTimePeriodInMinutes() { 265 return mUsageLimitUsageTimePeriodInMinutes; 266 } 267 268 /** 269 * The time at which usage statistic accumulation begins. It is in the format of number 270 * of milliseconds since January 1, 1970, 00:00:00 GMT. 271 * 272 * Use Long.MIN_VALUE to indicate unset value. 273 */ 274 private long mUsageLimitStartTimeInMillis = Long.MIN_VALUE; 275 /** 276 * @hide 277 */ setUsageLimitStartTimeInMillis(long usageLimitStartTimeInMillis)278 public void setUsageLimitStartTimeInMillis(long usageLimitStartTimeInMillis) { 279 mUsageLimitStartTimeInMillis = usageLimitStartTimeInMillis; 280 } 281 /** 282 * @hide 283 */ getUsageLimitStartTimeInMillis()284 public long getUsageLimitStartTimeInMillis() { 285 return mUsageLimitStartTimeInMillis; 286 } 287 288 /** 289 * The cumulative data limit in megabytes for the {@link #usageLimitUsageTimePeriodInMinutes}. 290 * A value of zero indicate unlimited data usage. 291 * 292 * Use Long.MIN_VALUE to indicate unset value. 293 */ 294 private long mUsageLimitDataLimit = Long.MIN_VALUE; 295 /** 296 * @hide 297 */ setUsageLimitDataLimit(long usageLimitDataLimit)298 public void setUsageLimitDataLimit(long usageLimitDataLimit) { 299 mUsageLimitDataLimit = usageLimitDataLimit; 300 } 301 /** 302 * @hide 303 */ getUsageLimitDataLimit()304 public long getUsageLimitDataLimit() { 305 return mUsageLimitDataLimit; 306 } 307 308 /** 309 * The cumulative time limit in minutes for the {@link #usageLimitUsageTimePeriodInMinutes}. 310 * A value of zero indicate unlimited time usage. 311 */ 312 private long mUsageLimitTimeLimitInMinutes = Long.MIN_VALUE; 313 /** 314 * @hide 315 */ setUsageLimitTimeLimitInMinutes(long usageLimitTimeLimitInMinutes)316 public void setUsageLimitTimeLimitInMinutes(long usageLimitTimeLimitInMinutes) { 317 mUsageLimitTimeLimitInMinutes = usageLimitTimeLimitInMinutes; 318 } 319 /** 320 * @hide 321 */ getUsageLimitTimeLimitInMinutes()322 public long getUsageLimitTimeLimitInMinutes() { 323 return mUsageLimitTimeLimitInMinutes; 324 } 325 326 /** 327 * Constructor for creating PasspointConfiguration with default values. 328 */ PasspointConfiguration()329 public PasspointConfiguration() {} 330 331 /** 332 * Copy constructor. 333 * 334 * @param source The source to copy from 335 */ PasspointConfiguration(PasspointConfiguration source)336 public PasspointConfiguration(PasspointConfiguration source) { 337 if (source == null) { 338 return; 339 } 340 341 if (source.mHomeSp != null) { 342 mHomeSp = new HomeSp(source.mHomeSp); 343 } 344 if (source.mCredential != null) { 345 mCredential = new Credential(source.mCredential); 346 } 347 if (source.mPolicy != null) { 348 mPolicy = new Policy(source.mPolicy); 349 } 350 if (source.mTrustRootCertList != null) { 351 mTrustRootCertList = Collections.unmodifiableMap(source.mTrustRootCertList); 352 } 353 if (source.mSubscriptionUpdate != null) { 354 mSubscriptionUpdate = new UpdateParameter(source.mSubscriptionUpdate); 355 } 356 mUpdateIdentifier = source.mUpdateIdentifier; 357 mCredentialPriority = source.mCredentialPriority; 358 mSubscriptionCreationTimeInMillis = source.mSubscriptionCreationTimeInMillis; 359 mSubscriptionExpirationTimeInMillis = source.mSubscriptionExpirationTimeInMillis; 360 mSubscriptionType = source.mSubscriptionType; 361 mUsageLimitDataLimit = source.mUsageLimitDataLimit; 362 mUsageLimitStartTimeInMillis = source.mUsageLimitStartTimeInMillis; 363 mUsageLimitTimeLimitInMinutes = source.mUsageLimitTimeLimitInMinutes; 364 mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes; 365 } 366 367 @Override describeContents()368 public int describeContents() { 369 return 0; 370 } 371 372 @Override writeToParcel(Parcel dest, int flags)373 public void writeToParcel(Parcel dest, int flags) { 374 dest.writeParcelable(mHomeSp, flags); 375 dest.writeParcelable(mCredential, flags); 376 dest.writeParcelable(mPolicy, flags); 377 dest.writeParcelable(mSubscriptionUpdate, flags); 378 writeTrustRootCerts(dest, mTrustRootCertList); 379 dest.writeInt(mUpdateIdentifier); 380 dest.writeInt(mCredentialPriority); 381 dest.writeLong(mSubscriptionCreationTimeInMillis); 382 dest.writeLong(mSubscriptionExpirationTimeInMillis); 383 dest.writeString(mSubscriptionType); 384 dest.writeLong(mUsageLimitUsageTimePeriodInMinutes); 385 dest.writeLong(mUsageLimitStartTimeInMillis); 386 dest.writeLong(mUsageLimitDataLimit); 387 dest.writeLong(mUsageLimitTimeLimitInMinutes); 388 } 389 390 @Override equals(Object thatObject)391 public boolean equals(Object thatObject) { 392 if (this == thatObject) { 393 return true; 394 } 395 if (!(thatObject instanceof PasspointConfiguration)) { 396 return false; 397 } 398 PasspointConfiguration that = (PasspointConfiguration) thatObject; 399 return (mHomeSp == null ? that.mHomeSp == null : mHomeSp.equals(that.mHomeSp)) 400 && (mCredential == null ? that.mCredential == null 401 : mCredential.equals(that.mCredential)) 402 && (mPolicy == null ? that.mPolicy == null : mPolicy.equals(that.mPolicy)) 403 && (mSubscriptionUpdate == null ? that.mSubscriptionUpdate == null 404 : mSubscriptionUpdate.equals(that.mSubscriptionUpdate)) 405 && isTrustRootCertListEquals(mTrustRootCertList, that.mTrustRootCertList) 406 && mUpdateIdentifier == that.mUpdateIdentifier 407 && mCredentialPriority == that.mCredentialPriority 408 && mSubscriptionCreationTimeInMillis == that.mSubscriptionCreationTimeInMillis 409 && mSubscriptionExpirationTimeInMillis == that.mSubscriptionExpirationTimeInMillis 410 && TextUtils.equals(mSubscriptionType, that.mSubscriptionType) 411 && mUsageLimitUsageTimePeriodInMinutes == that.mUsageLimitUsageTimePeriodInMinutes 412 && mUsageLimitStartTimeInMillis == that.mUsageLimitStartTimeInMillis 413 && mUsageLimitDataLimit == that.mUsageLimitDataLimit 414 && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes; 415 } 416 417 @Override hashCode()418 public int hashCode() { 419 return Objects.hash(mHomeSp, mCredential, mPolicy, mSubscriptionUpdate, mTrustRootCertList, 420 mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis, 421 mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes, 422 mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes); 423 } 424 425 @Override toString()426 public String toString() { 427 StringBuilder builder = new StringBuilder(); 428 builder.append("UpdateIdentifier: ").append(mUpdateIdentifier).append("\n"); 429 builder.append("CredentialPriority: ").append(mCredentialPriority).append("\n"); 430 builder.append("SubscriptionCreationTime: ").append( 431 mSubscriptionCreationTimeInMillis != Long.MIN_VALUE 432 ? new Date(mSubscriptionCreationTimeInMillis) : "Not specified").append("\n"); 433 builder.append("SubscriptionExpirationTime: ").append( 434 mSubscriptionExpirationTimeInMillis != Long.MIN_VALUE 435 ? new Date(mSubscriptionExpirationTimeInMillis) : "Not specified").append("\n"); 436 builder.append("UsageLimitStartTime: ").append(mUsageLimitStartTimeInMillis != Long.MIN_VALUE 437 ? new Date(mUsageLimitStartTimeInMillis) : "Not specified").append("\n"); 438 builder.append("UsageTimePeriod: ").append(mUsageLimitUsageTimePeriodInMinutes) 439 .append("\n"); 440 builder.append("UsageLimitDataLimit: ").append(mUsageLimitDataLimit).append("\n"); 441 builder.append("UsageLimitTimeLimit: ").append(mUsageLimitTimeLimitInMinutes).append("\n"); 442 if (mHomeSp != null) { 443 builder.append("HomeSP Begin ---\n"); 444 builder.append(mHomeSp); 445 builder.append("HomeSP End ---\n"); 446 } 447 if (mCredential != null) { 448 builder.append("Credential Begin ---\n"); 449 builder.append(mCredential); 450 builder.append("Credential End ---\n"); 451 } 452 if (mPolicy != null) { 453 builder.append("Policy Begin ---\n"); 454 builder.append(mPolicy); 455 builder.append("Policy End ---\n"); 456 } 457 if (mSubscriptionUpdate != null) { 458 builder.append("SubscriptionUpdate Begin ---\n"); 459 builder.append(mSubscriptionUpdate); 460 builder.append("SubscriptionUpdate End ---\n"); 461 } 462 if (mTrustRootCertList != null) { 463 builder.append("TrustRootCertServers: ").append(mTrustRootCertList.keySet()) 464 .append("\n"); 465 } 466 return builder.toString(); 467 } 468 469 /** 470 * Validate the configuration data. 471 * 472 * @return true on success or false on failure 473 * @hide 474 */ validate()475 public boolean validate() { 476 if (mHomeSp == null || !mHomeSp.validate()) { 477 return false; 478 } 479 if (mCredential == null || !mCredential.validate()) { 480 return false; 481 } 482 if (mPolicy != null && !mPolicy.validate()) { 483 return false; 484 } 485 if (mSubscriptionUpdate != null && !mSubscriptionUpdate.validate()) { 486 return false; 487 } 488 if (mTrustRootCertList != null) { 489 for (Map.Entry<String, byte[]> entry : mTrustRootCertList.entrySet()) { 490 String url = entry.getKey(); 491 byte[] certFingerprint = entry.getValue(); 492 if (TextUtils.isEmpty(url)) { 493 Log.d(TAG, "Empty URL"); 494 return false; 495 } 496 if (url.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) { 497 Log.d(TAG, "URL bytes exceeded the max: " 498 + url.getBytes(StandardCharsets.UTF_8).length); 499 return false; 500 } 501 502 if (certFingerprint == null) { 503 Log.d(TAG, "Fingerprint not specified"); 504 return false; 505 } 506 if (certFingerprint.length != CERTIFICATE_SHA256_BYTES) { 507 Log.d(TAG, "Incorrect size of trust root certificate SHA-256 fingerprint: " 508 + certFingerprint.length); 509 return false; 510 } 511 } 512 } 513 return true; 514 } 515 516 public static final Creator<PasspointConfiguration> CREATOR = 517 new Creator<PasspointConfiguration>() { 518 @Override 519 public PasspointConfiguration createFromParcel(Parcel in) { 520 PasspointConfiguration config = new PasspointConfiguration(); 521 config.setHomeSp(in.readParcelable(null)); 522 config.setCredential(in.readParcelable(null)); 523 config.setPolicy(in.readParcelable(null)); 524 config.setSubscriptionUpdate(in.readParcelable(null)); 525 config.setTrustRootCertList(readTrustRootCerts(in)); 526 config.setUpdateIdentifier(in.readInt()); 527 config.setCredentialPriority(in.readInt()); 528 config.setSubscriptionCreationTimeInMillis(in.readLong()); 529 config.setSubscriptionExpirationTimeInMillis(in.readLong()); 530 config.setSubscriptionType(in.readString()); 531 config.setUsageLimitUsageTimePeriodInMinutes(in.readLong()); 532 config.setUsageLimitStartTimeInMillis(in.readLong()); 533 config.setUsageLimitDataLimit(in.readLong()); 534 config.setUsageLimitTimeLimitInMinutes(in.readLong()); 535 return config; 536 } 537 538 @Override 539 public PasspointConfiguration[] newArray(int size) { 540 return new PasspointConfiguration[size]; 541 } 542 543 /** 544 * Helper function for reading trust root certificate info list from a Parcel. 545 * 546 * @param in The Parcel to read from 547 * @return The list of trust root certificate URL with the corresponding certificate 548 * fingerprint 549 */ 550 private Map<String, byte[]> readTrustRootCerts(Parcel in) { 551 int size = in.readInt(); 552 if (size == NULL_VALUE) { 553 return null; 554 } 555 Map<String, byte[]> trustRootCerts = new HashMap<>(size); 556 for (int i = 0; i < size; i++) { 557 String key = in.readString(); 558 byte[] value = in.createByteArray(); 559 trustRootCerts.put(key, value); 560 } 561 return trustRootCerts; 562 } 563 }; 564 565 /** 566 * Helper function for writing trust root certificate information list. 567 * 568 * @param dest The Parcel to write to 569 * @param trustRootCerts The list of trust root certificate URL with the corresponding 570 * certificate fingerprint 571 */ writeTrustRootCerts(Parcel dest, Map<String, byte[]> trustRootCerts)572 private static void writeTrustRootCerts(Parcel dest, Map<String, byte[]> trustRootCerts) { 573 if (trustRootCerts == null) { 574 dest.writeInt(NULL_VALUE); 575 return; 576 } 577 dest.writeInt(trustRootCerts.size()); 578 for (Map.Entry<String, byte[]> entry : trustRootCerts.entrySet()) { 579 dest.writeString(entry.getKey()); 580 dest.writeByteArray(entry.getValue()); 581 } 582 } 583 584 /** 585 * Helper function for comparing two trust root certificate list. Cannot use Map#equals 586 * method since the value type (byte[]) doesn't override equals method. 587 * 588 * @param list1 The first trust root certificate list 589 * @param list2 The second trust root certificate list 590 * @return true if the two list are equal 591 */ isTrustRootCertListEquals(Map<String, byte[]> list1, Map<String, byte[]> list2)592 private static boolean isTrustRootCertListEquals(Map<String, byte[]> list1, 593 Map<String, byte[]> list2) { 594 if (list1 == null || list2 == null) { 595 return list1 == list2; 596 } 597 if (list1.size() != list2.size()) { 598 return false; 599 } 600 for (Map.Entry<String, byte[]> entry : list1.entrySet()) { 601 if (!Arrays.equals(entry.getValue(), list2.get(entry.getKey()))) { 602 return false; 603 } 604 } 605 return true; 606 } 607 } 608