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 static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE; 20 import static android.net.wifi.WifiConfiguration.MeteredOverride; 21 22 import android.annotation.CurrentTimeMillisLong; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.SystemApi; 26 import android.net.wifi.hotspot2.pps.Credential; 27 import android.net.wifi.hotspot2.pps.HomeSp; 28 import android.net.wifi.hotspot2.pps.Policy; 29 import android.net.wifi.hotspot2.pps.UpdateParameter; 30 import android.os.Bundle; 31 import android.os.Parcel; 32 import android.os.Parcelable; 33 import android.telephony.TelephonyManager; 34 import android.text.TextUtils; 35 import android.util.Log; 36 37 import java.nio.charset.StandardCharsets; 38 import java.util.Arrays; 39 import java.util.Collections; 40 import java.util.Date; 41 import java.util.HashMap; 42 import java.util.Locale; 43 import java.util.Map; 44 import java.util.Objects; 45 46 /** 47 * Class representing Passpoint configuration. This contains configurations specified in 48 * PerProviderSubscription (PPS) Management Object (MO) tree. 49 * 50 * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0 51 * Release 2 Technical Specification. 52 */ 53 public final class PasspointConfiguration implements Parcelable { 54 private static final String TAG = "PasspointConfiguration"; 55 56 /** 57 * Number of bytes for certificate SHA-256 fingerprint byte array. 58 */ 59 private static final int CERTIFICATE_SHA256_BYTES = 32; 60 61 /** 62 * Maximum bytes for URL string. 63 */ 64 private static final int MAX_URL_BYTES = 1023; 65 66 /** 67 * Integer value used for indicating null value in the Parcel. 68 */ 69 private static final int NULL_VALUE = -1; 70 71 /** 72 * Configurations under HomeSp subtree. 73 */ 74 private HomeSp mHomeSp = null; 75 76 /** 77 * Set the Home SP (Service Provider) information. 78 * 79 * @param homeSp The Home SP information to set to 80 */ setHomeSp(HomeSp homeSp)81 public void setHomeSp(HomeSp homeSp) { mHomeSp = homeSp; } 82 /** 83 * Get the Home SP (Service Provider) information. 84 * 85 * @return Home SP information 86 */ getHomeSp()87 public HomeSp getHomeSp() { return mHomeSp; } 88 89 /** 90 * Configurations under AAAServerTrustedNames subtree. 91 */ 92 private String[] mAaaServerTrustedNames = null; 93 /** 94 * Set the AAA server trusted names information. 95 * 96 * @param aaaServerTrustedNames The AAA server trusted names information to set to 97 * @hide 98 */ setAaaServerTrustedNames(@ullable String[] aaaServerTrustedNames)99 public void setAaaServerTrustedNames(@Nullable String[] aaaServerTrustedNames) { 100 mAaaServerTrustedNames = aaaServerTrustedNames; 101 } 102 /** 103 * Get the AAA server trusted names information. 104 * 105 * @return AAA server trusted names information 106 * @hide 107 */ getAaaServerTrustedNames()108 public @Nullable String[] getAaaServerTrustedNames() { 109 return mAaaServerTrustedNames; 110 } 111 112 /** 113 * Configurations under Credential subtree. 114 */ 115 private Credential mCredential = null; 116 /** 117 * Set the credential information. 118 * 119 * @param credential The credential information to set to 120 */ setCredential(Credential credential)121 public void setCredential(Credential credential) { 122 mCredential = credential; 123 } 124 /** 125 * Get the credential information. 126 * 127 * @return credential information 128 */ getCredential()129 public Credential getCredential() { 130 return mCredential; 131 } 132 133 /** 134 * Configurations under Policy subtree. 135 */ 136 private Policy mPolicy = null; 137 /** 138 * @hide 139 */ setPolicy(Policy policy)140 public void setPolicy(Policy policy) { 141 mPolicy = policy; 142 } 143 /** 144 * @hide 145 */ getPolicy()146 public Policy getPolicy() { 147 return mPolicy; 148 } 149 150 /** 151 * Meta data for performing subscription update. 152 */ 153 private UpdateParameter mSubscriptionUpdate = null; 154 /** 155 * @hide 156 */ setSubscriptionUpdate(UpdateParameter subscriptionUpdate)157 public void setSubscriptionUpdate(UpdateParameter subscriptionUpdate) { 158 mSubscriptionUpdate = subscriptionUpdate; 159 } 160 /** 161 * @hide 162 */ getSubscriptionUpdate()163 public UpdateParameter getSubscriptionUpdate() { 164 return mSubscriptionUpdate; 165 } 166 167 /** 168 * List of HTTPS URL for retrieving trust root certificate and the corresponding SHA-256 169 * fingerprint of the certificate. The certificates are used for verifying AAA server's 170 * identity during EAP authentication. 171 */ 172 private Map<String, byte[]> mTrustRootCertList = null; 173 /** 174 * @hide 175 */ setTrustRootCertList(Map<String, byte[]> trustRootCertList)176 public void setTrustRootCertList(Map<String, byte[]> trustRootCertList) { 177 mTrustRootCertList = trustRootCertList; 178 } 179 /** 180 * @hide 181 */ getTrustRootCertList()182 public Map<String, byte[]> getTrustRootCertList() { 183 return mTrustRootCertList; 184 } 185 186 /** 187 * Set by the subscription server, updated every time the configuration is updated by 188 * the subscription server. 189 * 190 * Use Integer.MIN_VALUE to indicate unset value. 191 */ 192 private int mUpdateIdentifier = Integer.MIN_VALUE; 193 /** 194 * @hide 195 */ setUpdateIdentifier(int updateIdentifier)196 public void setUpdateIdentifier(int updateIdentifier) { 197 mUpdateIdentifier = updateIdentifier; 198 } 199 /** 200 * @hide 201 */ getUpdateIdentifier()202 public int getUpdateIdentifier() { 203 return mUpdateIdentifier; 204 } 205 206 /** 207 * The priority of the credential. 208 * 209 * Use Integer.MIN_VALUE to indicate unset value. 210 */ 211 private int mCredentialPriority = Integer.MIN_VALUE; 212 /** 213 * @hide 214 */ setCredentialPriority(int credentialPriority)215 public void setCredentialPriority(int credentialPriority) { 216 mCredentialPriority = credentialPriority; 217 } 218 /** 219 * @hide 220 */ getCredentialPriority()221 public int getCredentialPriority() { 222 return mCredentialPriority; 223 } 224 225 /** 226 * The time this subscription is created. It is in the format of number 227 * of milliseconds since January 1, 1970, 00:00:00 GMT. 228 * 229 * Use Long.MIN_VALUE to indicate unset value. 230 */ 231 private long mSubscriptionCreationTimeInMillis = Long.MIN_VALUE; 232 /** 233 * @hide 234 */ setSubscriptionCreationTimeInMillis(long subscriptionCreationTimeInMillis)235 public void setSubscriptionCreationTimeInMillis(long subscriptionCreationTimeInMillis) { 236 mSubscriptionCreationTimeInMillis = subscriptionCreationTimeInMillis; 237 } 238 /** 239 * @hide 240 */ getSubscriptionCreationTimeInMillis()241 public long getSubscriptionCreationTimeInMillis() { 242 return mSubscriptionCreationTimeInMillis; 243 } 244 245 /** 246 * The time this subscription will expire. It is in the format of number 247 * of milliseconds since January 1, 1970, 00:00:00 GMT. 248 * 249 * Use Long.MIN_VALUE to indicate unset value. 250 */ 251 private long mSubscriptionExpirationTimeMillis = Long.MIN_VALUE; 252 /** 253 * @hide 254 */ setSubscriptionExpirationTimeInMillis(long subscriptionExpirationTimeInMillis)255 public void setSubscriptionExpirationTimeInMillis(long subscriptionExpirationTimeInMillis) { 256 mSubscriptionExpirationTimeMillis = subscriptionExpirationTimeInMillis; 257 } 258 /** 259 * Utility method to get the time this subscription will expire. It is in the format of number 260 * of milliseconds since January 1, 1970, 00:00:00 GMT. 261 * 262 * @return The time this subscription will expire, or Long.MIN_VALUE to indicate unset value 263 */ 264 @CurrentTimeMillisLong getSubscriptionExpirationTimeMillis()265 public long getSubscriptionExpirationTimeMillis() { 266 return mSubscriptionExpirationTimeMillis; 267 } 268 269 /** 270 * The type of the subscription. This is defined by the provider and the value is provider 271 * specific. 272 */ 273 private String mSubscriptionType = null; 274 /** 275 * @hide 276 */ setSubscriptionType(String subscriptionType)277 public void setSubscriptionType(String subscriptionType) { 278 mSubscriptionType = subscriptionType; 279 } 280 /** 281 * @hide 282 */ getSubscriptionType()283 public String getSubscriptionType() { 284 return mSubscriptionType; 285 } 286 287 /** 288 * The time period for usage statistics accumulation. A value of zero means that usage 289 * statistics are not accumulated on a periodic basis (e.g., a one-time limit for 290 * “pay as you go” - PAYG service). A non-zero value specifies the usage interval in minutes. 291 */ 292 private long mUsageLimitUsageTimePeriodInMinutes = Long.MIN_VALUE; 293 /** 294 * @hide 295 */ setUsageLimitUsageTimePeriodInMinutes(long usageLimitUsageTimePeriodInMinutes)296 public void setUsageLimitUsageTimePeriodInMinutes(long usageLimitUsageTimePeriodInMinutes) { 297 mUsageLimitUsageTimePeriodInMinutes = usageLimitUsageTimePeriodInMinutes; 298 } 299 /** 300 * @hide 301 */ getUsageLimitUsageTimePeriodInMinutes()302 public long getUsageLimitUsageTimePeriodInMinutes() { 303 return mUsageLimitUsageTimePeriodInMinutes; 304 } 305 306 /** 307 * The time at which usage statistic accumulation begins. It is in the format of number 308 * of milliseconds since January 1, 1970, 00:00:00 GMT. 309 * 310 * Use Long.MIN_VALUE to indicate unset value. 311 */ 312 private long mUsageLimitStartTimeInMillis = Long.MIN_VALUE; 313 /** 314 * @hide 315 */ setUsageLimitStartTimeInMillis(long usageLimitStartTimeInMillis)316 public void setUsageLimitStartTimeInMillis(long usageLimitStartTimeInMillis) { 317 mUsageLimitStartTimeInMillis = usageLimitStartTimeInMillis; 318 } 319 /** 320 * @hide 321 */ getUsageLimitStartTimeInMillis()322 public long getUsageLimitStartTimeInMillis() { 323 return mUsageLimitStartTimeInMillis; 324 } 325 326 /** 327 * The cumulative data limit in megabytes for the {@link #usageLimitUsageTimePeriodInMinutes}. 328 * A value of zero indicate unlimited data usage. 329 * 330 * Use Long.MIN_VALUE to indicate unset value. 331 */ 332 private long mUsageLimitDataLimit = Long.MIN_VALUE; 333 /** 334 * @hide 335 */ setUsageLimitDataLimit(long usageLimitDataLimit)336 public void setUsageLimitDataLimit(long usageLimitDataLimit) { 337 mUsageLimitDataLimit = usageLimitDataLimit; 338 } 339 /** 340 * @hide 341 */ getUsageLimitDataLimit()342 public long getUsageLimitDataLimit() { 343 return mUsageLimitDataLimit; 344 } 345 346 /** 347 * The cumulative time limit in minutes for the {@link #usageLimitUsageTimePeriodInMinutes}. 348 * A value of zero indicate unlimited time usage. 349 */ 350 private long mUsageLimitTimeLimitInMinutes = Long.MIN_VALUE; 351 /** 352 * @hide 353 */ setUsageLimitTimeLimitInMinutes(long usageLimitTimeLimitInMinutes)354 public void setUsageLimitTimeLimitInMinutes(long usageLimitTimeLimitInMinutes) { 355 mUsageLimitTimeLimitInMinutes = usageLimitTimeLimitInMinutes; 356 } 357 /** 358 * @hide 359 */ getUsageLimitTimeLimitInMinutes()360 public long getUsageLimitTimeLimitInMinutes() { 361 return mUsageLimitTimeLimitInMinutes; 362 } 363 364 /** 365 * The map of OSU service provider names whose each element is presented in different 366 * languages for the service provider, which is used for finding a matching 367 * PasspointConfiguration with a given service provider name. 368 */ 369 private Map<String, String> mServiceFriendlyNames = null; 370 371 /** 372 * @hide 373 */ setServiceFriendlyNames(Map<String, String> serviceFriendlyNames)374 public void setServiceFriendlyNames(Map<String, String> serviceFriendlyNames) { 375 mServiceFriendlyNames = serviceFriendlyNames; 376 } 377 378 /** 379 * @hide 380 */ getServiceFriendlyNames()381 public Map<String, String> getServiceFriendlyNames() { 382 return mServiceFriendlyNames; 383 } 384 385 /** 386 * Return the friendly Name for current language from the list of friendly names of OSU 387 * provider. 388 * The string matching the default locale will be returned if it is found, otherwise the 389 * first string in the list will be returned. A null will be returned if the list is empty. 390 * 391 * @return String matching the default locale, null otherwise 392 * @hide 393 */ getServiceFriendlyName()394 public String getServiceFriendlyName() { 395 if (mServiceFriendlyNames == null || mServiceFriendlyNames.isEmpty()) return null; 396 String lang = Locale.getDefault().getLanguage(); 397 String friendlyName = mServiceFriendlyNames.get(lang); 398 if (friendlyName != null) { 399 return friendlyName; 400 } 401 friendlyName = mServiceFriendlyNames.get("en"); 402 if (friendlyName != null) { 403 return friendlyName; 404 } 405 return mServiceFriendlyNames.get(mServiceFriendlyNames.keySet().stream().findFirst().get()); 406 } 407 408 /** 409 * The carrier ID identifies the operator who provides this network configuration. 410 * see {@link TelephonyManager#getSimCarrierId()} 411 */ 412 private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; 413 414 /** 415 * Set the carrier ID associated with current configuration. 416 * @param carrierId {@code mCarrierId} 417 * @hide 418 */ setCarrierId(int carrierId)419 public void setCarrierId(int carrierId) { 420 this.mCarrierId = carrierId; 421 } 422 423 /** 424 * Get the carrier ID associated with current configuration. 425 * @return {@code mCarrierId} 426 * @hide 427 */ getCarrierId()428 public int getCarrierId() { 429 return mCarrierId; 430 } 431 432 /** 433 * The auto-join configuration specifies whether or not the Passpoint Configuration is 434 * considered for auto-connection. If true then yes, if false then it isn't considered as part 435 * of auto-connection - but can still be manually connected to. 436 */ 437 private boolean mIsAutojoinEnabled = true; 438 439 /** 440 * The mac randomization setting specifies whether a randomized or device MAC address will 441 * be used to connect to the passpoint network. If true, a randomized MAC will be used. 442 * Otherwise, the device MAC address will be used. 443 */ 444 private boolean mIsMacRandomizationEnabled = true; 445 446 /** 447 * Indicates if the end user has expressed an explicit opinion about the 448 * meteredness of this network, such as through the Settings app. 449 * This value is one of {@link #METERED_OVERRIDE_NONE}, {@link #METERED_OVERRIDE_METERED}, 450 * or {@link #METERED_OVERRIDE_NOT_METERED}. 451 * <p> 452 * This should always override any values from {@link WifiInfo#getMeteredHint()}. 453 * 454 * By default this field is set to {@link #METERED_OVERRIDE_NONE}. 455 */ 456 private int mMeteredOverride = METERED_OVERRIDE_NONE; 457 458 /** 459 * Configures the auto-association status of this Passpoint configuration. A value of true 460 * indicates that the configuration will be considered for auto-connection, a value of false 461 * indicates that only manual connection will work - the framework will not auto-associate to 462 * this Passpoint network. 463 * 464 * @param autojoinEnabled true to be considered for framework auto-connection, false otherwise. 465 * @hide 466 */ setAutojoinEnabled(boolean autojoinEnabled)467 public void setAutojoinEnabled(boolean autojoinEnabled) { 468 mIsAutojoinEnabled = autojoinEnabled; 469 } 470 471 /** 472 * Configures the MAC randomization setting for this Passpoint configuration. 473 * If set to true, the framework will use a randomized MAC address to connect to this Passpoint 474 * network. Otherwise, the framework will use the device MAC address. 475 * 476 * @param enabled true to use randomized MAC address, false to use device MAC address. 477 * @hide 478 */ setMacRandomizationEnabled(boolean enabled)479 public void setMacRandomizationEnabled(boolean enabled) { 480 mIsMacRandomizationEnabled = enabled; 481 } 482 483 /** 484 * Sets the metered override setting for this Passpoint configuration. 485 * 486 * @param meteredOverride One of the values in {@link MeteredOverride} 487 * @hide 488 */ setMeteredOverride(@eteredOverride int meteredOverride)489 public void setMeteredOverride(@MeteredOverride int meteredOverride) { 490 mMeteredOverride = meteredOverride; 491 } 492 493 /** 494 * Indicates whether the Passpoint configuration may be auto-connected to by the framework. A 495 * value of true indicates that auto-connection can happen, a value of false indicates that it 496 * cannot. However, even when auto-connection is not possible manual connection by the user is 497 * possible. 498 * 499 * @return the auto-join configuration: true for auto-connection (or join) enabled, false 500 * otherwise. 501 * @hide 502 */ 503 @SystemApi isAutojoinEnabled()504 public boolean isAutojoinEnabled() { 505 return mIsAutojoinEnabled; 506 } 507 508 /** 509 * Indicates whether the user chose this configuration to be treated as metered or not. 510 * 511 * @return One of the values in {@link MeteredOverride} 512 * @hide 513 */ 514 @SystemApi 515 @MeteredOverride getMeteredOverride()516 public int getMeteredOverride() { 517 return mMeteredOverride; 518 } 519 520 /** 521 * Indicates whether a randomized MAC address or device MAC address will be used for 522 * connections to this Passpoint network. If true, a randomized MAC address will be used. 523 * Otherwise, the device MAC address will be used. 524 * 525 * @return true for MAC randomization enabled. False for disabled. 526 * @hide 527 */ 528 @SystemApi isMacRandomizationEnabled()529 public boolean isMacRandomizationEnabled() { 530 return mIsMacRandomizationEnabled; 531 } 532 533 /** 534 * Constructor for creating PasspointConfiguration with default values. 535 */ PasspointConfiguration()536 public PasspointConfiguration() {} 537 538 /** 539 * Copy constructor. 540 * 541 * @param source The source to copy from 542 */ PasspointConfiguration(PasspointConfiguration source)543 public PasspointConfiguration(PasspointConfiguration source) { 544 if (source == null) { 545 return; 546 } 547 548 if (source.mHomeSp != null) { 549 mHomeSp = new HomeSp(source.mHomeSp); 550 } 551 if (source.mCredential != null) { 552 mCredential = new Credential(source.mCredential); 553 } 554 if (source.mPolicy != null) { 555 mPolicy = new Policy(source.mPolicy); 556 } 557 if (source.mTrustRootCertList != null) { 558 mTrustRootCertList = Collections.unmodifiableMap(source.mTrustRootCertList); 559 } 560 if (source.mSubscriptionUpdate != null) { 561 mSubscriptionUpdate = new UpdateParameter(source.mSubscriptionUpdate); 562 } 563 mUpdateIdentifier = source.mUpdateIdentifier; 564 mCredentialPriority = source.mCredentialPriority; 565 mSubscriptionCreationTimeInMillis = source.mSubscriptionCreationTimeInMillis; 566 mSubscriptionExpirationTimeMillis = source.mSubscriptionExpirationTimeMillis; 567 mSubscriptionType = source.mSubscriptionType; 568 mUsageLimitDataLimit = source.mUsageLimitDataLimit; 569 mUsageLimitStartTimeInMillis = source.mUsageLimitStartTimeInMillis; 570 mUsageLimitTimeLimitInMinutes = source.mUsageLimitTimeLimitInMinutes; 571 mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes; 572 mServiceFriendlyNames = source.mServiceFriendlyNames; 573 mAaaServerTrustedNames = source.mAaaServerTrustedNames; 574 mCarrierId = source.mCarrierId; 575 mIsAutojoinEnabled = source.mIsAutojoinEnabled; 576 mIsMacRandomizationEnabled = source.mIsMacRandomizationEnabled; 577 mMeteredOverride = source.mMeteredOverride; 578 } 579 580 @Override describeContents()581 public int describeContents() { 582 return 0; 583 } 584 585 @Override writeToParcel(Parcel dest, int flags)586 public void writeToParcel(Parcel dest, int flags) { 587 dest.writeParcelable(mHomeSp, flags); 588 dest.writeParcelable(mCredential, flags); 589 dest.writeParcelable(mPolicy, flags); 590 dest.writeParcelable(mSubscriptionUpdate, flags); 591 writeTrustRootCerts(dest, mTrustRootCertList); 592 dest.writeInt(mUpdateIdentifier); 593 dest.writeInt(mCredentialPriority); 594 dest.writeLong(mSubscriptionCreationTimeInMillis); 595 dest.writeLong(mSubscriptionExpirationTimeMillis); 596 dest.writeString(mSubscriptionType); 597 dest.writeLong(mUsageLimitUsageTimePeriodInMinutes); 598 dest.writeLong(mUsageLimitStartTimeInMillis); 599 dest.writeLong(mUsageLimitDataLimit); 600 dest.writeLong(mUsageLimitTimeLimitInMinutes); 601 dest.writeStringArray(mAaaServerTrustedNames); 602 Bundle bundle = new Bundle(); 603 bundle.putSerializable("serviceFriendlyNames", 604 (HashMap<String, String>) mServiceFriendlyNames); 605 dest.writeBundle(bundle); 606 dest.writeInt(mCarrierId); 607 dest.writeBoolean(mIsAutojoinEnabled); 608 dest.writeBoolean(mIsMacRandomizationEnabled); 609 dest.writeInt(mMeteredOverride); 610 } 611 612 @Override equals(Object thatObject)613 public boolean equals(Object thatObject) { 614 if (this == thatObject) { 615 return true; 616 } 617 if (!(thatObject instanceof PasspointConfiguration)) { 618 return false; 619 } 620 PasspointConfiguration that = (PasspointConfiguration) thatObject; 621 return (mHomeSp == null ? that.mHomeSp == null : mHomeSp.equals(that.mHomeSp)) 622 && (mAaaServerTrustedNames == null ? that.mAaaServerTrustedNames == null 623 : Arrays.equals(mAaaServerTrustedNames, that.mAaaServerTrustedNames)) 624 && (mCredential == null ? that.mCredential == null 625 : mCredential.equals(that.mCredential)) 626 && (mPolicy == null ? that.mPolicy == null : mPolicy.equals(that.mPolicy)) 627 && (mSubscriptionUpdate == null ? that.mSubscriptionUpdate == null 628 : mSubscriptionUpdate.equals(that.mSubscriptionUpdate)) 629 && isTrustRootCertListEquals(mTrustRootCertList, that.mTrustRootCertList) 630 && mUpdateIdentifier == that.mUpdateIdentifier 631 && mCredentialPriority == that.mCredentialPriority 632 && mSubscriptionCreationTimeInMillis == that.mSubscriptionCreationTimeInMillis 633 && mSubscriptionExpirationTimeMillis == that.mSubscriptionExpirationTimeMillis 634 && TextUtils.equals(mSubscriptionType, that.mSubscriptionType) 635 && mUsageLimitUsageTimePeriodInMinutes == that.mUsageLimitUsageTimePeriodInMinutes 636 && mUsageLimitStartTimeInMillis == that.mUsageLimitStartTimeInMillis 637 && mUsageLimitDataLimit == that.mUsageLimitDataLimit 638 && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes 639 && mCarrierId == that.mCarrierId 640 && mIsAutojoinEnabled == that.mIsAutojoinEnabled 641 && mIsMacRandomizationEnabled == that.mIsMacRandomizationEnabled 642 && mMeteredOverride == that.mMeteredOverride 643 && (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null 644 : mServiceFriendlyNames.equals(that.mServiceFriendlyNames)); 645 } 646 647 @Override hashCode()648 public int hashCode() { 649 return Objects.hash(mHomeSp, mCredential, mPolicy, mSubscriptionUpdate, mTrustRootCertList, 650 mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis, 651 mSubscriptionExpirationTimeMillis, mUsageLimitUsageTimePeriodInMinutes, 652 mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes, 653 mServiceFriendlyNames, mCarrierId, mIsAutojoinEnabled, mIsMacRandomizationEnabled, 654 mMeteredOverride); 655 } 656 657 @Override toString()658 public String toString() { 659 StringBuilder builder = new StringBuilder(); 660 builder.append("UpdateIdentifier: ").append(mUpdateIdentifier).append("\n"); 661 builder.append("CredentialPriority: ").append(mCredentialPriority).append("\n"); 662 builder.append("SubscriptionCreationTime: ").append( 663 mSubscriptionCreationTimeInMillis != Long.MIN_VALUE 664 ? new Date(mSubscriptionCreationTimeInMillis) : "Not specified").append("\n"); 665 builder.append("SubscriptionExpirationTime: ").append( 666 mSubscriptionExpirationTimeMillis != Long.MIN_VALUE 667 ? new Date(mSubscriptionExpirationTimeMillis) : "Not specified").append("\n"); 668 builder.append("UsageLimitStartTime: ").append(mUsageLimitStartTimeInMillis != Long.MIN_VALUE 669 ? new Date(mUsageLimitStartTimeInMillis) : "Not specified").append("\n"); 670 builder.append("UsageTimePeriod: ").append(mUsageLimitUsageTimePeriodInMinutes) 671 .append("\n"); 672 builder.append("UsageLimitDataLimit: ").append(mUsageLimitDataLimit).append("\n"); 673 builder.append("UsageLimitTimeLimit: ").append(mUsageLimitTimeLimitInMinutes).append("\n"); 674 builder.append("Provisioned by a subscription server: ") 675 .append(isOsuProvisioned() ? "Yes" : "No").append("\n"); 676 if (mHomeSp != null) { 677 builder.append("HomeSP Begin ---\n"); 678 builder.append(mHomeSp); 679 builder.append("HomeSP End ---\n"); 680 } 681 if (mCredential != null) { 682 builder.append("Credential Begin ---\n"); 683 builder.append(mCredential); 684 builder.append("Credential End ---\n"); 685 } 686 if (mPolicy != null) { 687 builder.append("Policy Begin ---\n"); 688 builder.append(mPolicy); 689 builder.append("Policy End ---\n"); 690 } 691 if (mSubscriptionUpdate != null) { 692 builder.append("SubscriptionUpdate Begin ---\n"); 693 builder.append(mSubscriptionUpdate); 694 builder.append("SubscriptionUpdate End ---\n"); 695 } 696 if (mTrustRootCertList != null) { 697 builder.append("TrustRootCertServers: ").append(mTrustRootCertList.keySet()) 698 .append("\n"); 699 } 700 if (mAaaServerTrustedNames != null) { 701 builder.append("AAAServerTrustedNames: ") 702 .append(String.join(";", mAaaServerTrustedNames)).append("\n"); 703 } 704 if (mServiceFriendlyNames != null) { 705 builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames); 706 } 707 builder.append("CarrierId:" + mCarrierId); 708 builder.append("IsAutojoinEnabled:" + mIsAutojoinEnabled); 709 builder.append("mIsMacRandomizationEnabled:" + mIsMacRandomizationEnabled); 710 builder.append("mMeteredOverride:" + mMeteredOverride); 711 return builder.toString(); 712 } 713 714 /** 715 * Validate the R1 configuration data. 716 * 717 * @return true on success or false on failure 718 * @hide 719 */ validate()720 public boolean validate() { 721 // Optional: PerProviderSubscription/<X+>/SubscriptionUpdate 722 if (mSubscriptionUpdate != null && !mSubscriptionUpdate.validate()) { 723 return false; 724 } 725 return validateForCommonR1andR2(); 726 } 727 728 /** 729 * Validate the R2 configuration data. 730 * 731 * @return true on success or false on failure 732 * @hide 733 */ validateForR2()734 public boolean validateForR2() { 735 // Required: PerProviderSubscription/UpdateIdentifier 736 if (mUpdateIdentifier == Integer.MIN_VALUE) { 737 return false; 738 } 739 740 // Required: PerProviderSubscription/<X+>/SubscriptionUpdate 741 if (mSubscriptionUpdate == null || !mSubscriptionUpdate.validate()) { 742 return false; 743 } 744 return validateForCommonR1andR2(); 745 } 746 validateForCommonR1andR2()747 private boolean validateForCommonR1andR2() { 748 // Required: PerProviderSubscription/<X+>/HomeSP 749 if (mHomeSp == null || !mHomeSp.validate()) { 750 return false; 751 } 752 753 // Required: PerProviderSubscription/<X+>/Credential 754 if (mCredential == null || !mCredential.validate()) { 755 return false; 756 } 757 758 // Optional: PerProviderSubscription/<X+>/Policy 759 if (mPolicy != null && !mPolicy.validate()) { 760 return false; 761 } 762 763 if (mTrustRootCertList != null) { 764 for (Map.Entry<String, byte[]> entry : mTrustRootCertList.entrySet()) { 765 String url = entry.getKey(); 766 byte[] certFingerprint = entry.getValue(); 767 if (TextUtils.isEmpty(url)) { 768 Log.d(TAG, "Empty URL"); 769 return false; 770 } 771 if (url.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) { 772 Log.d(TAG, "URL bytes exceeded the max: " 773 + url.getBytes(StandardCharsets.UTF_8).length); 774 return false; 775 } 776 777 if (certFingerprint == null) { 778 Log.d(TAG, "Fingerprint not specified"); 779 return false; 780 } 781 if (certFingerprint.length != CERTIFICATE_SHA256_BYTES) { 782 Log.d(TAG, "Incorrect size of trust root certificate SHA-256 fingerprint: " 783 + certFingerprint.length); 784 return false; 785 } 786 } 787 } 788 return true; 789 } 790 791 public static final @android.annotation.NonNull Creator<PasspointConfiguration> CREATOR = 792 new Creator<PasspointConfiguration>() { 793 @Override 794 public PasspointConfiguration createFromParcel(Parcel in) { 795 PasspointConfiguration config = new PasspointConfiguration(); 796 config.setHomeSp(in.readParcelable(null)); 797 config.setCredential(in.readParcelable(null)); 798 config.setPolicy(in.readParcelable(null)); 799 config.setSubscriptionUpdate(in.readParcelable(null)); 800 config.setTrustRootCertList(readTrustRootCerts(in)); 801 config.setUpdateIdentifier(in.readInt()); 802 config.setCredentialPriority(in.readInt()); 803 config.setSubscriptionCreationTimeInMillis(in.readLong()); 804 config.setSubscriptionExpirationTimeInMillis(in.readLong()); 805 config.setSubscriptionType(in.readString()); 806 config.setUsageLimitUsageTimePeriodInMinutes(in.readLong()); 807 config.setUsageLimitStartTimeInMillis(in.readLong()); 808 config.setUsageLimitDataLimit(in.readLong()); 809 config.setUsageLimitTimeLimitInMinutes(in.readLong()); 810 config.setAaaServerTrustedNames(in.createStringArray()); 811 Bundle bundle = in.readBundle(); 812 Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable( 813 "serviceFriendlyNames"); 814 config.setServiceFriendlyNames(friendlyNamesMap); 815 config.mCarrierId = in.readInt(); 816 config.mIsAutojoinEnabled = in.readBoolean(); 817 config.mIsMacRandomizationEnabled = in.readBoolean(); 818 config.mMeteredOverride = in.readInt(); 819 return config; 820 } 821 822 @Override 823 public PasspointConfiguration[] newArray(int size) { 824 return new PasspointConfiguration[size]; 825 } 826 827 /** 828 * Helper function for reading trust root certificate info list from a Parcel. 829 * 830 * @param in The Parcel to read from 831 * @return The list of trust root certificate URL with the corresponding certificate 832 * fingerprint 833 */ 834 private Map<String, byte[]> readTrustRootCerts(Parcel in) { 835 int size = in.readInt(); 836 if (size == NULL_VALUE) { 837 return null; 838 } 839 Map<String, byte[]> trustRootCerts = new HashMap<>(size); 840 for (int i = 0; i < size; i++) { 841 String key = in.readString(); 842 byte[] value = in.createByteArray(); 843 trustRootCerts.put(key, value); 844 } 845 return trustRootCerts; 846 } 847 }; 848 849 /** 850 * Helper function for writing trust root certificate information list. 851 * 852 * @param dest The Parcel to write to 853 * @param trustRootCerts The list of trust root certificate URL with the corresponding 854 * certificate fingerprint 855 */ writeTrustRootCerts(Parcel dest, Map<String, byte[]> trustRootCerts)856 private static void writeTrustRootCerts(Parcel dest, Map<String, byte[]> trustRootCerts) { 857 if (trustRootCerts == null) { 858 dest.writeInt(NULL_VALUE); 859 return; 860 } 861 dest.writeInt(trustRootCerts.size()); 862 for (Map.Entry<String, byte[]> entry : trustRootCerts.entrySet()) { 863 dest.writeString(entry.getKey()); 864 dest.writeByteArray(entry.getValue()); 865 } 866 } 867 868 /** 869 * Helper function for comparing two trust root certificate list. Cannot use Map#equals 870 * method since the value type (byte[]) doesn't override equals method. 871 * 872 * @param list1 The first trust root certificate list 873 * @param list2 The second trust root certificate list 874 * @return true if the two list are equal 875 */ isTrustRootCertListEquals(Map<String, byte[]> list1, Map<String, byte[]> list2)876 private static boolean isTrustRootCertListEquals(Map<String, byte[]> list1, 877 Map<String, byte[]> list2) { 878 if (list1 == null || list2 == null) { 879 return list1 == list2; 880 } 881 if (list1.size() != list2.size()) { 882 return false; 883 } 884 for (Map.Entry<String, byte[]> entry : list1.entrySet()) { 885 if (!Arrays.equals(entry.getValue(), list2.get(entry.getKey()))) { 886 return false; 887 } 888 } 889 return true; 890 } 891 892 /** 893 * Indicates if the Passpoint Configuration was provisioned by a subscription (OSU) server, 894 * which means that it's an R2 (or R3) profile. 895 * 896 * @return true if the Passpoint Configuration was provisioned by a subscription server. 897 */ isOsuProvisioned()898 public boolean isOsuProvisioned() { 899 return getUpdateIdentifier() != Integer.MIN_VALUE; 900 } 901 902 /** 903 * Get a unique identifier for a PasspointConfiguration object. The identifier depends on the 904 * configuration that identify the service provider under the HomeSp subtree, and on the 905 * credential configuration under the Credential subtree. 906 * The method throws an {@link IllegalStateException} if the configuration under HomeSp subtree 907 * or the configuration under Credential subtree are not initialized. 908 * 909 * @return A unique identifier 910 */ getUniqueId()911 public @NonNull String getUniqueId() { 912 if (mCredential == null || mHomeSp == null || TextUtils.isEmpty(mHomeSp.getFqdn())) { 913 throw new IllegalStateException("Credential or HomeSP are not initialized"); 914 } 915 916 StringBuilder sb = new StringBuilder(); 917 sb.append(String.format("%s_%x%x", mHomeSp.getFqdn(), mHomeSp.getUniqueId(), 918 mCredential.getUniqueId())); 919 return sb.toString(); 920 } 921 } 922