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