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