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