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.pps; 18 19 import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_HESSID_VALUE; 20 import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_NUMBER_OF_ENTRIES; 21 import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_NUMBER_OF_OI; 22 import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_OI_VALUE; 23 import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_STRING_LENGTH; 24 import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_URL_BYTES; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.text.TextUtils; 31 import android.util.Log; 32 33 import java.nio.charset.StandardCharsets; 34 import java.util.Arrays; 35 import java.util.Collection; 36 import java.util.Collections; 37 import java.util.HashMap; 38 import java.util.Map; 39 import java.util.Objects; 40 41 /** 42 * Class representing HomeSP subtree in PerProviderSubscription (PPS) 43 * Management Object (MO) tree. 44 * 45 * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0 46 * Release 2 Technical Specification. 47 */ 48 public final class HomeSp implements Parcelable { 49 private static final String TAG = "HomeSp"; 50 51 /** 52 * Maximum number of bytes allowed for a SSID. 53 */ 54 private static final int MAX_SSID_BYTES = 32; 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 * FQDN (Fully Qualified Domain Name) of this home service provider. 63 */ 64 private String mFqdn = null; 65 /** 66 * Set the FQDN (Fully Qualified Domain Name) associated with this home service provider. 67 * 68 * @param fqdn The FQDN to set to 69 */ setFqdn(String fqdn)70 public void setFqdn(String fqdn) { 71 mFqdn = fqdn; 72 } 73 /** 74 * Get the FQDN (Fully Qualified Domain Name) associated with this home service provider. 75 * 76 * @return the FQDN associated with this home service provider 77 */ getFqdn()78 public String getFqdn() { 79 return mFqdn; 80 } 81 82 /** 83 * Friendly name of this home service provider. 84 */ 85 private String mFriendlyName = null; 86 /** 87 * Set the friendly name associated with this home service provider. 88 * 89 * @param friendlyName The friendly name to set to 90 */ setFriendlyName(String friendlyName)91 public void setFriendlyName(String friendlyName) { 92 mFriendlyName = friendlyName; 93 } 94 /** 95 * Get the friendly name associated with this home service provider. 96 * 97 * @return the friendly name associated with this home service provider 98 */ getFriendlyName()99 public String getFriendlyName() { 100 return mFriendlyName; 101 } 102 103 /** 104 * Icon URL of this home service provider. 105 */ 106 private String mIconUrl = null; 107 /** 108 * @hide 109 */ setIconUrl(String iconUrl)110 public void setIconUrl(String iconUrl) { 111 mIconUrl = iconUrl; 112 } 113 /** 114 * @hide 115 */ getIconUrl()116 public String getIconUrl() { 117 return mIconUrl; 118 } 119 120 /** 121 * <SSID, HESSID> duple of the networks that are consider home networks. 122 * 123 * According to the Section 9.1.2 of the Hotspot 2.0 Release 2 Technical Specification, 124 * all nodes in the PSS MO are encoded using UTF-8 unless stated otherwise. Thus, the SSID 125 * string is assumed to be encoded using UTF-8. 126 */ 127 private Map<String, Long> mHomeNetworkIds = null; 128 /** 129 * @hide 130 */ setHomeNetworkIds(Map<String, Long> homeNetworkIds)131 public void setHomeNetworkIds(Map<String, Long> homeNetworkIds) { 132 mHomeNetworkIds = homeNetworkIds; 133 } 134 /** 135 * @hide 136 */ getHomeNetworkIds()137 public Map<String, Long> getHomeNetworkIds() { 138 return mHomeNetworkIds; 139 } 140 141 /** 142 * Used for determining if this provider is a member of a given Hotspot provider. 143 * Every Organization Identifiers (OIs) in this list are required to match an OI in the 144 * the Roaming Consortium advertised by a Hotspot, in order to consider this provider 145 * as a member of that Hotspot provider (e.g. successful authentication with such Hotspot 146 * is possible). 147 * 148 * Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object 149 * (MO) tree for more detail. 150 */ 151 private long[] mMatchAllOis = null; 152 153 /** 154 * Set a list of HomeOIs such that all OIs in the list must match an OI in the Roaming 155 * Consortium advertised by a hotspot operator. The list set by this API will have precedence 156 * over {@link #setMatchAnyOis(long[])}, meaning the list set in {@link #setMatchAnyOis(long[])} 157 * will only be used for matching if the list set by this API is null or empty. 158 * 159 * @param matchAllOis An array of longs containing the HomeOIs 160 */ setMatchAllOis(@ullable long[] matchAllOis)161 public void setMatchAllOis(@Nullable long[] matchAllOis) { 162 mMatchAllOis = matchAllOis; 163 } 164 165 /** 166 * Get the list of HomeOIs such that all OIs in the list must match an OI in the Roaming 167 * Consortium advertised by a hotspot operator. 168 * 169 * @return An array of longs containing the HomeOIs 170 */ getMatchAllOis()171 public @Nullable long[] getMatchAllOis() { 172 return mMatchAllOis; 173 } 174 175 /** 176 * Used for determining if this provider is a member of a given Hotspot provider. 177 * Matching of any Organization Identifiers (OIs) in this list with an OI in the 178 * Roaming Consortium advertised by a Hotspot, will consider this provider as a member 179 * of that Hotspot provider (e.g. successful authentication with such Hotspot 180 * is possible). 181 * 182 * The list set by {@link #setMatchAllOis(long[])} will have precedence over this one, meaning 183 * this list will only be used for matching if the list set by {@link #setMatchAllOis(long[])} 184 * is null or empty. 185 * 186 * Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object 187 * (MO) tree for more detail. 188 */ 189 private long[] mMatchAnyOis = null; 190 191 /** 192 * Set a list of HomeOIs such that any OI in the list matches an OI in the Roaming Consortium 193 * advertised by a hotspot operator. The list set by {@link #setMatchAllOis(long[])} 194 * will have precedence over this API, meaning this list will only be used for matching if the 195 * list set by {@link #setMatchAllOis(long[])} is null or empty. 196 * 197 * @param matchAnyOis An array of longs containing the HomeOIs 198 */ setMatchAnyOis(@ullable long[] matchAnyOis)199 public void setMatchAnyOis(@Nullable long[] matchAnyOis) { 200 mMatchAnyOis = matchAnyOis; 201 } 202 203 /** 204 * Get a list of HomeOIs such that any OI in the list matches an OI in the Roaming Consortium 205 * advertised by a hotspot operator. 206 * 207 * @return An array of longs containing the HomeOIs 208 */ getMatchAnyOis()209 public @Nullable long[] getMatchAnyOis() { 210 return mMatchAnyOis; 211 } 212 213 /** 214 * List of FQDN (Fully Qualified Domain Name) of partner providers. 215 * These providers should also be regarded as home Hotspot operators. 216 * This relationship is most likely achieved via a commercial agreement or 217 * operator merges between the providers. 218 */ 219 private String[] mOtherHomePartners = null; 220 221 /** 222 * Set the list of FQDN (Fully Qualified Domain Name) of other Home partner providers. 223 * 224 * @param otherHomePartners Array of Strings containing the FQDNs of other Home partner 225 * providers 226 * @hide 227 */ setOtherHomePartners(@ullable String[] otherHomePartners)228 public void setOtherHomePartners(@Nullable String[] otherHomePartners) { 229 mOtherHomePartners = otherHomePartners; 230 } 231 232 /** 233 * Set the list of FQDN (Fully Qualified Domain Name) of other Home partner providers. 234 * 235 * @param otherHomePartners Collection of Strings containing the FQDNs of other Home partner 236 * providers 237 */ setOtherHomePartnersList(@onNull Collection<String> otherHomePartners)238 public void setOtherHomePartnersList(@NonNull Collection<String> otherHomePartners) { 239 if (otherHomePartners == null) { 240 return; 241 } 242 mOtherHomePartners = otherHomePartners.toArray(new String[otherHomePartners.size()]); 243 } 244 245 /** 246 * Get the list of FQDN (Fully Qualified Domain Name) of other Home partner providers set in 247 * the profile. 248 * 249 * @return Array of Strings containing the FQDNs of other Home partner providers set in the 250 * profile 251 * @hide 252 */ getOtherHomePartners()253 public @Nullable String[] getOtherHomePartners() { 254 return mOtherHomePartners; 255 } 256 257 /** 258 * Get the list of FQDN (Fully Qualified Domain Name) of other Home partner providers set in 259 * the profile. 260 * 261 * @return Collection of Strings containing the FQDNs of other Home partner providers set in the 262 * profile 263 */ getOtherHomePartnersList()264 public @NonNull Collection<String> getOtherHomePartnersList() { 265 if (mOtherHomePartners == null) { 266 return Collections.emptyList(); 267 } 268 return Arrays.asList(mOtherHomePartners); 269 } 270 271 /** 272 * List of Organization Identifiers (OIs) identifying a roaming consortium of 273 * which this provider is a member. 274 */ 275 private long[] mRoamingConsortiumOis = null; 276 /** 277 * Set the Organization Identifiers (OIs) identifying a roaming consortium of which this 278 * provider is a member. 279 * 280 * @param roamingConsortiumOis Array of roaming consortium OIs 281 */ setRoamingConsortiumOis(long[] roamingConsortiumOis)282 public void setRoamingConsortiumOis(long[] roamingConsortiumOis) { 283 mRoamingConsortiumOis = roamingConsortiumOis; 284 } 285 /** 286 * Get the Organization Identifiers (OIs) identifying a roaming consortium of which this 287 * provider is a member. 288 * 289 * @return array of roaming consortium OIs 290 */ getRoamingConsortiumOis()291 public long[] getRoamingConsortiumOis() { 292 return mRoamingConsortiumOis; 293 } 294 295 /** 296 * Constructor for creating HomeSp with default values. 297 */ HomeSp()298 public HomeSp() {} 299 300 /** 301 * Copy constructor. 302 * 303 * @param source The source to copy from 304 */ HomeSp(HomeSp source)305 public HomeSp(HomeSp source) { 306 if (source == null) { 307 return; 308 } 309 mFqdn = source.mFqdn; 310 mFriendlyName = source.mFriendlyName; 311 mIconUrl = source.mIconUrl; 312 if (source.mHomeNetworkIds != null) { 313 mHomeNetworkIds = Collections.unmodifiableMap(source.mHomeNetworkIds); 314 } 315 if (source.mMatchAllOis != null) { 316 mMatchAllOis = Arrays.copyOf(source.mMatchAllOis, source.mMatchAllOis.length); 317 } 318 if (source.mMatchAnyOis != null) { 319 mMatchAnyOis = Arrays.copyOf(source.mMatchAnyOis, source.mMatchAnyOis.length); 320 } 321 if (source.mOtherHomePartners != null) { 322 mOtherHomePartners = Arrays.copyOf(source.mOtherHomePartners, 323 source.mOtherHomePartners.length); 324 } 325 if (source.mRoamingConsortiumOis != null) { 326 mRoamingConsortiumOis = Arrays.copyOf(source.mRoamingConsortiumOis, 327 source.mRoamingConsortiumOis.length); 328 } 329 } 330 331 @Override describeContents()332 public int describeContents() { 333 return 0; 334 } 335 336 @Override writeToParcel(Parcel dest, int flags)337 public void writeToParcel(Parcel dest, int flags) { 338 dest.writeString(mFqdn); 339 dest.writeString(mFriendlyName); 340 dest.writeString(mIconUrl); 341 writeHomeNetworkIds(dest, mHomeNetworkIds); 342 dest.writeLongArray(mMatchAllOis); 343 dest.writeLongArray(mMatchAnyOis); 344 dest.writeStringArray(mOtherHomePartners); 345 dest.writeLongArray(mRoamingConsortiumOis); 346 } 347 348 @Override equals(Object thatObject)349 public boolean equals(Object thatObject) { 350 if (this == thatObject) { 351 return true; 352 } 353 if (!(thatObject instanceof HomeSp)) { 354 return false; 355 } 356 HomeSp that = (HomeSp) thatObject; 357 358 return TextUtils.equals(mFqdn, that.mFqdn) 359 && TextUtils.equals(mFriendlyName, that.mFriendlyName) 360 && TextUtils.equals(mIconUrl, that.mIconUrl) 361 && (mHomeNetworkIds == null ? that.mHomeNetworkIds == null 362 : mHomeNetworkIds.equals(that.mHomeNetworkIds)) 363 && Arrays.equals(mMatchAllOis, that.mMatchAllOis) 364 && Arrays.equals(mMatchAnyOis, that.mMatchAnyOis) 365 && Arrays.equals(mOtherHomePartners, that.mOtherHomePartners) 366 && Arrays.equals(mRoamingConsortiumOis, that.mRoamingConsortiumOis); 367 } 368 369 @Override hashCode()370 public int hashCode() { 371 return Objects.hash(mFqdn, mFriendlyName, mIconUrl, 372 mHomeNetworkIds, Arrays.hashCode(mMatchAllOis), 373 Arrays.hashCode(mMatchAnyOis), Arrays.hashCode(mOtherHomePartners), 374 Arrays.hashCode(mRoamingConsortiumOis)); 375 } 376 377 /** 378 * Get a unique identifier for HomeSp. This identifier depends only on items that remain 379 * constant throughout the lifetime of a subscription. 380 * 381 * @hide 382 * @return a Unique identifier for a HomeSp object 383 */ getUniqueId()384 public int getUniqueId() { 385 return Objects.hash(mFqdn); 386 } 387 388 389 @Override toString()390 public String toString() { 391 StringBuilder builder = new StringBuilder(); 392 builder.append("FQDN: ").append(mFqdn).append("\n"); 393 builder.append("FriendlyName: ").append(mFriendlyName).append("\n"); 394 builder.append("IconURL: ").append(mIconUrl).append("\n"); 395 builder.append("HomeNetworkIDs: ").append(mHomeNetworkIds).append("\n"); 396 builder.append("MatchAllOIs: ").append(Arrays.toString(mMatchAllOis)).append("\n"); 397 builder.append("MatchAnyOIs: ").append(Arrays.toString(mMatchAnyOis)).append("\n"); 398 builder.append("OtherHomePartners: ").append(Arrays.toString(mOtherHomePartners)) 399 .append("\n"); 400 builder.append("RoamingConsortiumOIs: ").append(Arrays.toString(mRoamingConsortiumOis)) 401 .append("\n"); 402 return builder.toString(); 403 } 404 405 /** 406 * Validate HomeSp data. 407 * 408 * @return true on success or false on failure 409 * @hide 410 */ validate()411 public boolean validate() { 412 if (TextUtils.isEmpty(mFqdn)) { 413 Log.d(TAG, "Missing FQDN"); 414 return false; 415 } 416 if (mFqdn.getBytes(StandardCharsets.UTF_8).length > MAX_STRING_LENGTH) { 417 Log.d(TAG, "FQDN is too long"); 418 return false; 419 } 420 if (TextUtils.isEmpty(mFriendlyName)) { 421 Log.d(TAG, "Missing friendly name"); 422 return false; 423 } 424 if (mFriendlyName.getBytes(StandardCharsets.UTF_8).length > MAX_STRING_LENGTH) { 425 Log.d(TAG, "Friendly name is too long"); 426 return false; 427 } 428 // Verify SSIDs specified in the NetworkID 429 if (mHomeNetworkIds != null) { 430 if (mHomeNetworkIds.size() > MAX_NUMBER_OF_ENTRIES) { 431 Log.d(TAG, "too many SSID in HomeNetworkIDs"); 432 return false; 433 } 434 for (Map.Entry<String, Long> entry : mHomeNetworkIds.entrySet()) { 435 if (entry.getKey() == null || 436 entry.getKey().getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) { 437 Log.d(TAG, "SSID is too long in HomeNetworkIDs"); 438 return false; 439 } 440 if (entry.getValue() != null 441 && (entry.getValue() > MAX_HESSID_VALUE || entry.getValue() < 0)) { 442 Log.d(TAG, "HESSID is out of range"); 443 return false; 444 } 445 } 446 } 447 if (mIconUrl != null && mIconUrl.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) { 448 Log.d(TAG, "Icon URL is too long"); 449 return false; 450 } 451 if (mMatchAllOis != null) { 452 if (mMatchAllOis.length > MAX_NUMBER_OF_OI) { 453 Log.d(TAG, "too many match all Organization Identifiers in the profile"); 454 return false; 455 } 456 for (long oi : mMatchAllOis) { 457 if (oi > MAX_OI_VALUE || oi < 0) { 458 Log.d(TAG, "Organization Identifiers is out of range"); 459 return false; 460 } 461 } 462 } 463 if (mMatchAnyOis != null) { 464 if (mMatchAnyOis.length > MAX_NUMBER_OF_OI) { 465 Log.d(TAG, "too many match any Organization Identifiers in the profile"); 466 return false; 467 } 468 for (long oi : mMatchAnyOis) { 469 if (oi > MAX_OI_VALUE || oi < 0) { 470 Log.d(TAG, "Organization Identifiers is out of range"); 471 return false; 472 } 473 } 474 } 475 if (mRoamingConsortiumOis != null) { 476 if (mRoamingConsortiumOis.length > MAX_NUMBER_OF_OI) { 477 Log.d(TAG, "too many Roaming Consortium Organization Identifiers in the " 478 + "profile"); 479 return false; 480 } 481 for (long oi : mRoamingConsortiumOis) { 482 if (oi > MAX_OI_VALUE || oi < 0) { 483 Log.d(TAG, "Organization Identifiers is out of range"); 484 return false; 485 } 486 } 487 } 488 if (mOtherHomePartners != null) { 489 if (mOtherHomePartners.length > MAX_NUMBER_OF_ENTRIES) { 490 Log.d(TAG, "too many other home partners in the profile"); 491 return false; 492 } 493 for (String fqdn : mOtherHomePartners) { 494 if (fqdn.length() > MAX_STRING_LENGTH) { 495 Log.d(TAG, "FQDN is too long in OtherHomePartners"); 496 return false; 497 } 498 } 499 } 500 return true; 501 } 502 503 public static final @android.annotation.NonNull Creator<HomeSp> CREATOR = 504 new Creator<HomeSp>() { 505 @Override 506 public HomeSp createFromParcel(Parcel in) { 507 HomeSp homeSp = new HomeSp(); 508 homeSp.setFqdn(in.readString()); 509 homeSp.setFriendlyName(in.readString()); 510 homeSp.setIconUrl(in.readString()); 511 homeSp.setHomeNetworkIds(readHomeNetworkIds(in)); 512 homeSp.setMatchAllOis(in.createLongArray()); 513 homeSp.setMatchAnyOis(in.createLongArray()); 514 homeSp.setOtherHomePartners(in.createStringArray()); 515 homeSp.setRoamingConsortiumOis(in.createLongArray()); 516 return homeSp; 517 } 518 519 @Override 520 public HomeSp[] newArray(int size) { 521 return new HomeSp[size]; 522 } 523 524 /** 525 * Helper function for reading a Home Network IDs map from a Parcel. 526 * 527 * @param in The Parcel to read from 528 * @return Map of home network IDs 529 */ 530 private Map<String, Long> readHomeNetworkIds(Parcel in) { 531 int size = in.readInt(); 532 if (size == NULL_VALUE) { 533 return null; 534 } 535 Map<String, Long> networkIds = new HashMap<>(size); 536 for (int i = 0; i < size; i++) { 537 String key = in.readString(); 538 Long value = null; 539 long readValue = in.readLong(); 540 if (readValue != NULL_VALUE) { 541 value = Long.valueOf(readValue); 542 } 543 networkIds.put(key, value); 544 } 545 return networkIds; 546 } 547 }; 548 549 /** 550 * Helper function for writing Home Network IDs map to a Parcel. 551 * 552 * @param dest The Parcel to write to 553 * @param networkIds The map of home network IDs 554 */ writeHomeNetworkIds(Parcel dest, Map<String, Long> networkIds)555 private static void writeHomeNetworkIds(Parcel dest, Map<String, Long> networkIds) { 556 if (networkIds == null) { 557 dest.writeInt(NULL_VALUE); 558 return; 559 } 560 dest.writeInt(networkIds.size()); 561 for (Map.Entry<String, Long> entry : networkIds.entrySet()) { 562 dest.writeString(entry.getKey()); 563 if (entry.getValue() == null) { 564 dest.writeLong(NULL_VALUE); 565 } else { 566 dest.writeLong(entry.getValue()); 567 } 568 } 569 } 570 } 571