PasspointConfiguration.java revision b5ca6f36b54023f6336740cf3bff79c08ecb6609
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 17package android.net.wifi.hotspot2; 18 19import android.net.wifi.hotspot2.pps.Credential; 20import android.net.wifi.hotspot2.pps.HomeSp; 21import android.net.wifi.hotspot2.pps.Policy; 22import android.net.wifi.hotspot2.pps.UpdateParameter; 23import android.os.Parcelable; 24import android.text.TextUtils; 25import android.util.Log; 26import android.os.Parcel; 27 28import java.nio.charset.StandardCharsets; 29import java.util.Arrays; 30import java.util.Collections; 31import java.util.HashMap; 32import java.util.Map; 33import java.util.Objects; 34 35/** 36 * Class representing Passpoint configuration. This contains configurations specified in 37 * PerProviderSubscription (PPS) Management Object (MO) tree. 38 * 39 * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0 40 * Release 2 Technical Specification. 41 */ 42public final class PasspointConfiguration implements Parcelable { 43 private static final String TAG = "PasspointConfiguration"; 44 45 /** 46 * Number of bytes for certificate SHA-256 fingerprint byte array. 47 */ 48 private static final int CERTIFICATE_SHA256_BYTES = 32; 49 50 /** 51 * Maximum bytes for URL string. 52 */ 53 private static final int MAX_URL_BYTES = 1023; 54 55 /** 56 * Integer value used for indicating null value in the Parcel. 57 */ 58 private static final int NULL_VALUE = -1; 59 60 /** 61 * Configurations under HomeSp subtree. 62 */ 63 private HomeSp mHomeSp = null; 64 public void setHomeSp(HomeSp homeSp) { mHomeSp = homeSp; } 65 public HomeSp getHomeSp() { return mHomeSp; } 66 67 /** 68 * Configurations under Credential subtree. 69 */ 70 private Credential mCredential = null; 71 public void setCredential(Credential credential) { 72 mCredential = credential; 73 } 74 public Credential getCredential() { 75 return mCredential; 76 } 77 78 /** 79 * Configurations under Policy subtree. 80 */ 81 private Policy mPolicy = null; 82 public void setPolicy(Policy policy) { 83 mPolicy = policy; 84 } 85 public Policy getPolicy() { 86 return mPolicy; 87 } 88 89 /** 90 * Meta data for performing subscription update. 91 */ 92 private UpdateParameter mSubscriptionUpdate = null; 93 public void setSubscriptionUpdate(UpdateParameter subscriptionUpdate) { 94 mSubscriptionUpdate = subscriptionUpdate; 95 } 96 public UpdateParameter getSubscriptionUpdate() { 97 return mSubscriptionUpdate; 98 } 99 100 /** 101 * List of HTTPS URL for retrieving trust root certificate and the corresponding SHA-256 102 * fingerprint of the certificate. The certificates are used for verifying AAA server's 103 * identity during EAP authentication. 104 */ 105 private Map<String, byte[]> mTrustRootCertList = null; 106 public void setTrustRootCertList(Map<String, byte[]> trustRootCertList) { 107 mTrustRootCertList = trustRootCertList; 108 } 109 public Map<String, byte[]> getTrustRootCertList() { 110 return mTrustRootCertList; 111 } 112 113 /** 114 * Set by the subscription server, updated every time the configuration is updated by 115 * the subscription server. 116 * 117 * Use Integer.MIN_VALUE to indicate unset value. 118 */ 119 private int mUpdateIdentifier = Integer.MIN_VALUE; 120 public void setUpdateIdentifier(int updateIdentifier) { 121 mUpdateIdentifier = updateIdentifier; 122 } 123 public int getUpdateIdentifier() { 124 return mUpdateIdentifier; 125 } 126 127 /** 128 * The priority of the credential. 129 * 130 * Use Integer.MIN_VALUE to indicate unset value. 131 */ 132 private int mCredentialPriority = Integer.MIN_VALUE; 133 public void setCredentialPriority(int credentialPriority) { 134 mCredentialPriority = credentialPriority; 135 } 136 public int getCredentialPriority() { 137 return mCredentialPriority; 138 } 139 140 /** 141 * The time this subscription is created. It is in the format of number 142 * of milliseconds since January 1, 1970, 00:00:00 GMT. 143 * 144 * Use Long.MIN_VALUE to indicate unset value. 145 */ 146 private long mSubscriptionCreationTimeInMs = Long.MIN_VALUE; 147 public void setSubscriptionCreationTimeInMs(long subscriptionCreationTimeInMs) { 148 mSubscriptionCreationTimeInMs = subscriptionCreationTimeInMs; 149 } 150 public long getSubscriptionCreationTimeInMs() { 151 return mSubscriptionCreationTimeInMs; 152 } 153 154 /** 155 * The time this subscription will expire. It is in the format of number 156 * of milliseconds since January 1, 1970, 00:00:00 GMT. 157 * 158 * Use Long.MIN_VALUE to indicate unset value. 159 */ 160 private long mSubscriptionExpirationTimeInMs = Long.MIN_VALUE; 161 public void setSubscriptionExpirationTimeInMs(long subscriptionExpirationTimeInMs) { 162 mSubscriptionExpirationTimeInMs = subscriptionExpirationTimeInMs; 163 } 164 public long getSubscriptionExpirationTimeInMs() { 165 return mSubscriptionExpirationTimeInMs; 166 } 167 168 /** 169 * The type of the subscription. This is defined by the provider and the value is provider 170 * specific. 171 */ 172 private String mSubscriptionType = null; 173 public void setSubscriptionType(String subscriptionType) { 174 mSubscriptionType = subscriptionType; 175 } 176 public String getSubscriptionType() { 177 return mSubscriptionType; 178 } 179 180 /** 181 * The time period for usage statistics accumulation. A value of zero means that usage 182 * statistics are not accumulated on a periodic basis (e.g., a one-time limit for 183 * “pay as you go” - PAYG service). A non-zero value specifies the usage interval in minutes. 184 */ 185 private long mUsageLimitUsageTimePeriodInMinutes = Long.MIN_VALUE; 186 public void setUsageLimitUsageTimePeriodInMinutes(long usageLimitUsageTimePeriodInMinutes) { 187 mUsageLimitUsageTimePeriodInMinutes = usageLimitUsageTimePeriodInMinutes; 188 } 189 public long getUsageLimitUsageTimePeriodInMinutes() { 190 return mUsageLimitUsageTimePeriodInMinutes; 191 } 192 193 /** 194 * The time at which usage statistic accumulation begins. 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 mUsageLimitStartTimeInMs = Long.MIN_VALUE; 200 public void setUsageLimitStartTimeInMs(long usageLimitStartTimeInMs) { 201 mUsageLimitStartTimeInMs = usageLimitStartTimeInMs; 202 } 203 public long getUsageLimitStartTimeInMs() { 204 return mUsageLimitStartTimeInMs; 205 } 206 207 /** 208 * The cumulative data limit in megabytes for the {@link #usageLimitUsageTimePeriodInMinutes}. 209 * A value of zero indicate unlimited data usage. 210 * 211 * Use Long.MIN_VALUE to indicate unset value. 212 */ 213 private long mUsageLimitDataLimit = Long.MIN_VALUE; 214 public void setUsageLimitDataLimit(long usageLimitDataLimit) { 215 mUsageLimitDataLimit = usageLimitDataLimit; 216 } 217 public long getUsageLimitDataLimit() { 218 return mUsageLimitDataLimit; 219 } 220 221 /** 222 * The cumulative time limit in minutes for the {@link #usageLimitUsageTimePeriodInMinutes}. 223 * A value of zero indicate unlimited time usage. 224 */ 225 private long mUsageLimitTimeLimitInMinutes = Long.MIN_VALUE; 226 public void setUsageLimitTimeLimitInMinutes(long usageLimitTimeLimitInMinutes) { 227 mUsageLimitTimeLimitInMinutes = usageLimitTimeLimitInMinutes; 228 } 229 public long getUsageLimitTimeLimitInMinutes() { 230 return mUsageLimitTimeLimitInMinutes; 231 } 232 233 /** 234 * Constructor for creating PasspointConfiguration with default values. 235 */ 236 public PasspointConfiguration() {} 237 238 /** 239 * Copy constructor. 240 * 241 * @param source The source to copy from 242 */ 243 public PasspointConfiguration(PasspointConfiguration source) { 244 if (source == null) { 245 return; 246 } 247 248 if (source.mHomeSp != null) { 249 mHomeSp = new HomeSp(source.mHomeSp); 250 } 251 if (source.mCredential != null) { 252 mCredential = new Credential(source.mCredential); 253 } 254 if (source.mPolicy != null) { 255 mPolicy = new Policy(source.mPolicy); 256 } 257 if (source.mTrustRootCertList != null) { 258 mTrustRootCertList = Collections.unmodifiableMap(source.mTrustRootCertList); 259 } 260 if (source.mSubscriptionUpdate != null) { 261 mSubscriptionUpdate = new UpdateParameter(source.mSubscriptionUpdate); 262 } 263 mUpdateIdentifier = source.mUpdateIdentifier; 264 mCredentialPriority = source.mCredentialPriority; 265 mSubscriptionCreationTimeInMs = source.mSubscriptionCreationTimeInMs; 266 mSubscriptionExpirationTimeInMs = source.mSubscriptionExpirationTimeInMs; 267 mSubscriptionType = source.mSubscriptionType; 268 mUsageLimitDataLimit = source.mUsageLimitDataLimit; 269 mUsageLimitStartTimeInMs = source.mUsageLimitStartTimeInMs; 270 mUsageLimitTimeLimitInMinutes = source.mUsageLimitTimeLimitInMinutes; 271 mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes; 272 } 273 274 @Override 275 public int describeContents() { 276 return 0; 277 } 278 279 @Override 280 public void writeToParcel(Parcel dest, int flags) { 281 dest.writeParcelable(mHomeSp, flags); 282 dest.writeParcelable(mCredential, flags); 283 dest.writeParcelable(mPolicy, flags); 284 dest.writeParcelable(mSubscriptionUpdate, flags); 285 writeTrustRootCerts(dest, mTrustRootCertList); 286 dest.writeInt(mUpdateIdentifier); 287 dest.writeInt(mCredentialPriority); 288 dest.writeLong(mSubscriptionCreationTimeInMs); 289 dest.writeLong(mSubscriptionExpirationTimeInMs); 290 dest.writeString(mSubscriptionType); 291 dest.writeLong(mUsageLimitUsageTimePeriodInMinutes); 292 dest.writeLong(mUsageLimitStartTimeInMs); 293 dest.writeLong(mUsageLimitDataLimit); 294 dest.writeLong(mUsageLimitTimeLimitInMinutes); 295 } 296 297 @Override 298 public boolean equals(Object thatObject) { 299 if (this == thatObject) { 300 return true; 301 } 302 if (!(thatObject instanceof PasspointConfiguration)) { 303 return false; 304 } 305 PasspointConfiguration that = (PasspointConfiguration) thatObject; 306 return (mHomeSp == null ? that.mHomeSp == null : mHomeSp.equals(that.mHomeSp)) 307 && (mCredential == null ? that.mCredential == null 308 : mCredential.equals(that.mCredential)) 309 && (mPolicy == null ? that.mPolicy == null : mPolicy.equals(that.mPolicy)) 310 && (mSubscriptionUpdate == null ? that.mSubscriptionUpdate == null 311 : mSubscriptionUpdate.equals(that.mSubscriptionUpdate)) 312 && isTrustRootCertListEquals(mTrustRootCertList, that.mTrustRootCertList) 313 && mUpdateIdentifier == that.mUpdateIdentifier 314 && mCredentialPriority == that.mCredentialPriority 315 && mSubscriptionCreationTimeInMs == that.mSubscriptionCreationTimeInMs 316 && mSubscriptionExpirationTimeInMs == that.mSubscriptionExpirationTimeInMs 317 && TextUtils.equals(mSubscriptionType, that.mSubscriptionType) 318 && mUsageLimitUsageTimePeriodInMinutes == that.mUsageLimitUsageTimePeriodInMinutes 319 && mUsageLimitStartTimeInMs == that.mUsageLimitStartTimeInMs 320 && mUsageLimitDataLimit == that.mUsageLimitDataLimit 321 && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes; 322 } 323 324 @Override 325 public int hashCode() { 326 return Objects.hash(mHomeSp, mCredential, mPolicy, mSubscriptionUpdate, mTrustRootCertList, 327 mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMs, 328 mSubscriptionExpirationTimeInMs, mUsageLimitUsageTimePeriodInMinutes, 329 mUsageLimitStartTimeInMs, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes); 330 } 331 332 /** 333 * Validate the configuration data. 334 * 335 * @return true on success or false on failure 336 * @hide 337 */ 338 public boolean validate() { 339 if (mHomeSp == null || !mHomeSp.validate()) { 340 return false; 341 } 342 if (mCredential == null || !mCredential.validate()) { 343 return false; 344 } 345 if (mPolicy != null && !mPolicy.validate()) { 346 return false; 347 } 348 if (mSubscriptionUpdate != null && !mSubscriptionUpdate.validate()) { 349 return false; 350 } 351 if (mTrustRootCertList != null) { 352 for (Map.Entry<String, byte[]> entry : mTrustRootCertList.entrySet()) { 353 String url = entry.getKey(); 354 byte[] certFingerprint = entry.getValue(); 355 if (TextUtils.isEmpty(url)) { 356 Log.d(TAG, "Empty URL"); 357 return false; 358 } 359 if (url.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) { 360 Log.d(TAG, "URL bytes exceeded the max: " 361 + url.getBytes(StandardCharsets.UTF_8).length); 362 return false; 363 } 364 365 if (certFingerprint == null) { 366 Log.d(TAG, "Fingerprint not specified"); 367 return false; 368 } 369 if (certFingerprint.length != CERTIFICATE_SHA256_BYTES) { 370 Log.d(TAG, "Incorrect size of trust root certificate SHA-256 fingerprint: " 371 + certFingerprint.length); 372 return false; 373 } 374 } 375 } 376 return true; 377 } 378 379 public static final Creator<PasspointConfiguration> CREATOR = 380 new Creator<PasspointConfiguration>() { 381 @Override 382 public PasspointConfiguration createFromParcel(Parcel in) { 383 PasspointConfiguration config = new PasspointConfiguration(); 384 config.setHomeSp(in.readParcelable(null)); 385 config.setCredential(in.readParcelable(null)); 386 config.setPolicy(in.readParcelable(null)); 387 config.setSubscriptionUpdate(in.readParcelable(null)); 388 config.setTrustRootCertList(readTrustRootCerts(in)); 389 config.setUpdateIdentifier(in.readInt()); 390 config.setCredentialPriority(in.readInt()); 391 config.setSubscriptionCreationTimeInMs(in.readLong()); 392 config.setSubscriptionExpirationTimeInMs(in.readLong()); 393 config.setSubscriptionType(in.readString()); 394 config.setUsageLimitUsageTimePeriodInMinutes(in.readLong()); 395 config.setUsageLimitStartTimeInMs(in.readLong()); 396 config.setUsageLimitDataLimit(in.readLong()); 397 config.setUsageLimitTimeLimitInMinutes(in.readLong()); 398 return config; 399 } 400 401 @Override 402 public PasspointConfiguration[] newArray(int size) { 403 return new PasspointConfiguration[size]; 404 } 405 406 /** 407 * Helper function for reading trust root certificate info list from a Parcel. 408 * 409 * @param in The Parcel to read from 410 * @return The list of trust root certificate URL with the corresponding certificate 411 * fingerprint 412 */ 413 private Map<String, byte[]> readTrustRootCerts(Parcel in) { 414 int size = in.readInt(); 415 if (size == NULL_VALUE) { 416 return null; 417 } 418 Map<String, byte[]> trustRootCerts = new HashMap<>(size); 419 for (int i = 0; i < size; i++) { 420 String key = in.readString(); 421 byte[] value = in.createByteArray(); 422 trustRootCerts.put(key, value); 423 } 424 return trustRootCerts; 425 } 426 }; 427 428 /** 429 * Helper function for writing trust root certificate information list. 430 * 431 * @param dest The Parcel to write to 432 * @param trustRootCerts The list of trust root certificate URL with the corresponding 433 * certificate fingerprint 434 */ 435 private static void writeTrustRootCerts(Parcel dest, Map<String, byte[]> trustRootCerts) { 436 if (trustRootCerts == null) { 437 dest.writeInt(NULL_VALUE); 438 return; 439 } 440 dest.writeInt(trustRootCerts.size()); 441 for (Map.Entry<String, byte[]> entry : trustRootCerts.entrySet()) { 442 dest.writeString(entry.getKey()); 443 dest.writeByteArray(entry.getValue()); 444 } 445 } 446 447 /** 448 * Helper function for comparing two trust root certificate list. Cannot use Map#equals 449 * method since the value type (byte[]) doesn't override equals method. 450 * 451 * @param list1 The first trust root certificate list 452 * @param list2 The second trust root certificate list 453 * @return true if the two list are equal 454 */ 455 private static boolean isTrustRootCertListEquals(Map<String, byte[]> list1, 456 Map<String, byte[]> list2) { 457 if (list1 == null || list2 == null) { 458 return list1 == list2; 459 } 460 if (list1.size() != list2.size()) { 461 return false; 462 } 463 for (Map.Entry<String, byte[]> entry : list1.entrySet()) { 464 if (!Arrays.equals(entry.getValue(), list2.get(entry.getKey()))) { 465 return false; 466 } 467 } 468 return true; 469 } 470} 471