• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.WifiManager;
27 import android.net.wifi.hotspot2.pps.Credential;
28 import android.net.wifi.hotspot2.pps.HomeSp;
29 import android.net.wifi.hotspot2.pps.Policy;
30 import android.net.wifi.hotspot2.pps.UpdateParameter;
31 import android.os.Build;
32 import android.os.Bundle;
33 import android.os.Parcel;
34 import android.os.Parcelable;
35 import android.telephony.SubscriptionInfo;
36 import android.telephony.SubscriptionManager;
37 import android.telephony.TelephonyManager;
38 import android.text.TextUtils;
39 import android.util.Log;
40 
41 import androidx.annotation.RequiresApi;
42 
43 import com.android.modules.utils.build.SdkLevel;
44 
45 import java.nio.charset.StandardCharsets;
46 import java.util.Arrays;
47 import java.util.Collections;
48 import java.util.Date;
49 import java.util.HashMap;
50 import java.util.Locale;
51 import java.util.Map;
52 import java.util.Objects;
53 
54 /**
55  * Class representing Passpoint configuration.  This contains configurations specified in
56  * PerProviderSubscription (PPS) Management Object (MO) tree.
57  *
58  * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
59  * Release 2 Technical Specification.
60  */
61 public final class PasspointConfiguration implements Parcelable {
62     private static final String TAG = "PasspointConfiguration";
63 
64     /**
65      * Number of bytes for certificate SHA-256 fingerprint byte array.
66      */
67     private static final int CERTIFICATE_SHA256_BYTES = 32;
68 
69     /**
70      * Maximum bytes for URL string.
71      */
72     private static final int MAX_URL_BYTES = 1023;
73 
74     /**
75      * Integer value used for indicating null value in the Parcel.
76      */
77     private static final int NULL_VALUE = -1;
78 
79     /**
80      * Configurations under HomeSp subtree.
81      */
82     private HomeSp mHomeSp = null;
83 
84     /**
85      * Set the Home SP (Service Provider) information.
86      *
87      * @param homeSp The Home SP information to set to
88      */
setHomeSp(HomeSp homeSp)89     public void setHomeSp(HomeSp homeSp) { mHomeSp = homeSp; }
90     /**
91      * Get the Home SP (Service Provider) information.
92      *
93      * @return Home SP information
94      */
getHomeSp()95     public HomeSp getHomeSp() { return mHomeSp; }
96 
97     /**
98      * Configurations under AAAServerTrustedNames subtree.
99      */
100     private String[] mAaaServerTrustedNames = null;
101     /**
102      * Set the AAA server trusted names information.
103      *
104      * @param aaaServerTrustedNames The AAA server trusted names information to set to
105      * @hide
106      */
setAaaServerTrustedNames(@ullable String[] aaaServerTrustedNames)107     public void setAaaServerTrustedNames(@Nullable String[] aaaServerTrustedNames) {
108         mAaaServerTrustedNames = aaaServerTrustedNames;
109     }
110     /**
111      * Get the AAA server trusted names information.
112      *
113      * @return AAA server trusted names information
114      * @hide
115      */
getAaaServerTrustedNames()116     public @Nullable String[] getAaaServerTrustedNames() {
117         return mAaaServerTrustedNames;
118     }
119 
120     /**
121      * Configurations under Credential subtree.
122      */
123     private Credential mCredential = null;
124     /**
125      * Set the credential information.
126      *
127      * @param credential The credential information to set to
128      */
setCredential(Credential credential)129     public void setCredential(Credential credential) {
130         mCredential = credential;
131     }
132     /**
133      * Get the credential information.
134      *
135      * @return credential information
136      */
getCredential()137     public Credential getCredential() {
138         return mCredential;
139     }
140 
141     /**
142      * Configurations under Policy subtree.
143      */
144     private Policy mPolicy = null;
145     /**
146      * @hide
147      */
setPolicy(Policy policy)148     public void setPolicy(Policy policy) {
149         mPolicy = policy;
150     }
151     /**
152      * @hide
153      */
getPolicy()154     public Policy getPolicy() {
155         return mPolicy;
156     }
157 
158     /**
159      * Meta data for performing subscription update.
160      */
161     private UpdateParameter mSubscriptionUpdate = null;
162     /**
163      * @hide
164      */
setSubscriptionUpdate(UpdateParameter subscriptionUpdate)165     public void setSubscriptionUpdate(UpdateParameter subscriptionUpdate) {
166         mSubscriptionUpdate = subscriptionUpdate;
167     }
168     /**
169      * @hide
170      */
getSubscriptionUpdate()171     public UpdateParameter getSubscriptionUpdate() {
172         return mSubscriptionUpdate;
173     }
174 
175     /**
176      * List of HTTPS URL for retrieving trust root certificate and the corresponding SHA-256
177      * fingerprint of the certificate.  The certificates are used for verifying AAA server's
178      * identity during EAP authentication.
179      */
180     private Map<String, byte[]> mTrustRootCertList = null;
181     /**
182      * @hide
183      */
setTrustRootCertList(Map<String, byte[]> trustRootCertList)184     public void setTrustRootCertList(Map<String, byte[]> trustRootCertList) {
185         mTrustRootCertList = trustRootCertList;
186     }
187     /**
188      * @hide
189      */
getTrustRootCertList()190     public Map<String, byte[]> getTrustRootCertList() {
191         return mTrustRootCertList;
192     }
193 
194     /**
195      * Set by the subscription server, updated every time the configuration is updated by
196      * the subscription server.
197      *
198      * Use Integer.MIN_VALUE to indicate unset value.
199      */
200     private int mUpdateIdentifier = Integer.MIN_VALUE;
201     /**
202      * @hide
203      */
setUpdateIdentifier(int updateIdentifier)204     public void setUpdateIdentifier(int updateIdentifier) {
205         mUpdateIdentifier = updateIdentifier;
206     }
207     /**
208      * @hide
209      */
getUpdateIdentifier()210     public int getUpdateIdentifier() {
211         return mUpdateIdentifier;
212     }
213 
214     /**
215      * The priority of the credential.
216      *
217      * Use Integer.MIN_VALUE to indicate unset value.
218      */
219     private int mCredentialPriority = Integer.MIN_VALUE;
220     /**
221      * @hide
222      */
setCredentialPriority(int credentialPriority)223     public void setCredentialPriority(int credentialPriority) {
224         mCredentialPriority = credentialPriority;
225     }
226     /**
227      * @hide
228      */
getCredentialPriority()229     public int getCredentialPriority() {
230         return mCredentialPriority;
231     }
232 
233     /**
234      * The time this subscription is created. It is in the format of number
235      * of milliseconds since January 1, 1970, 00:00:00 GMT.
236      *
237      * Use Long.MIN_VALUE to indicate unset value.
238      */
239     private long mSubscriptionCreationTimeInMillis = Long.MIN_VALUE;
240     /**
241      * @hide
242      */
setSubscriptionCreationTimeInMillis(long subscriptionCreationTimeInMillis)243     public void setSubscriptionCreationTimeInMillis(long subscriptionCreationTimeInMillis) {
244         mSubscriptionCreationTimeInMillis = subscriptionCreationTimeInMillis;
245     }
246     /**
247      * @hide
248      */
getSubscriptionCreationTimeInMillis()249     public long getSubscriptionCreationTimeInMillis() {
250         return mSubscriptionCreationTimeInMillis;
251     }
252 
253     /**
254      * The time this subscription will expire. It is in the format of number
255      * of milliseconds since January 1, 1970, 00:00:00 GMT.
256      *
257      * Use Long.MIN_VALUE to indicate unset value.
258      */
259     private long mSubscriptionExpirationTimeMillis = Long.MIN_VALUE;
260     /**
261      * @hide
262      */
setSubscriptionExpirationTimeInMillis(long subscriptionExpirationTimeInMillis)263     public void setSubscriptionExpirationTimeInMillis(long subscriptionExpirationTimeInMillis) {
264         mSubscriptionExpirationTimeMillis = subscriptionExpirationTimeInMillis;
265     }
266     /**
267      *  Utility method to get the time this subscription will expire. It is in the format of number
268      *  of milliseconds since January 1, 1970, 00:00:00 GMT.
269      *
270      *  @return The time this subscription will expire, or Long.MIN_VALUE to indicate unset value
271      */
272     @CurrentTimeMillisLong
getSubscriptionExpirationTimeMillis()273     public long getSubscriptionExpirationTimeMillis() {
274         return mSubscriptionExpirationTimeMillis;
275     }
276 
277     /**
278      * The type of the subscription.  This is defined by the provider and the value is provider
279      * specific.
280      */
281     private String mSubscriptionType = null;
282     /**
283      * @hide
284      */
setSubscriptionType(String subscriptionType)285     public void setSubscriptionType(String subscriptionType) {
286         mSubscriptionType = subscriptionType;
287     }
288     /**
289      * @hide
290      */
getSubscriptionType()291     public String getSubscriptionType() {
292         return mSubscriptionType;
293     }
294 
295     /**
296      * The time period for usage statistics accumulation. A value of zero means that usage
297      * statistics are not accumulated on a periodic basis (e.g., a one-time limit for
298      * “pay as you go” - PAYG service). A non-zero value specifies the usage interval in minutes.
299      */
300     private long mUsageLimitUsageTimePeriodInMinutes = Long.MIN_VALUE;
301     /**
302      * @hide
303      */
setUsageLimitUsageTimePeriodInMinutes(long usageLimitUsageTimePeriodInMinutes)304     public void setUsageLimitUsageTimePeriodInMinutes(long usageLimitUsageTimePeriodInMinutes) {
305         mUsageLimitUsageTimePeriodInMinutes = usageLimitUsageTimePeriodInMinutes;
306     }
307     /**
308      * @hide
309      */
getUsageLimitUsageTimePeriodInMinutes()310     public long getUsageLimitUsageTimePeriodInMinutes() {
311         return mUsageLimitUsageTimePeriodInMinutes;
312     }
313 
314     /**
315      * The time at which usage statistic accumulation  begins.  It is in the format of number
316      * of milliseconds since January 1, 1970, 00:00:00 GMT.
317      *
318      * Use Long.MIN_VALUE to indicate unset value.
319      */
320     private long mUsageLimitStartTimeInMillis = Long.MIN_VALUE;
321     /**
322      * @hide
323      */
setUsageLimitStartTimeInMillis(long usageLimitStartTimeInMillis)324     public void setUsageLimitStartTimeInMillis(long usageLimitStartTimeInMillis) {
325         mUsageLimitStartTimeInMillis = usageLimitStartTimeInMillis;
326     }
327     /**
328      * @hide
329      */
getUsageLimitStartTimeInMillis()330     public long getUsageLimitStartTimeInMillis() {
331         return mUsageLimitStartTimeInMillis;
332     }
333 
334     /**
335      * The cumulative data limit in megabytes for the {@link #usageLimitUsageTimePeriodInMinutes}.
336      * A value of zero indicate unlimited data usage.
337      *
338      * Use Long.MIN_VALUE to indicate unset value.
339      */
340     private long mUsageLimitDataLimit = Long.MIN_VALUE;
341     /**
342      * @hide
343      */
setUsageLimitDataLimit(long usageLimitDataLimit)344     public void setUsageLimitDataLimit(long usageLimitDataLimit) {
345         mUsageLimitDataLimit = usageLimitDataLimit;
346     }
347     /**
348      * @hide
349      */
getUsageLimitDataLimit()350     public long getUsageLimitDataLimit() {
351         return mUsageLimitDataLimit;
352     }
353 
354     /**
355      * The cumulative time limit in minutes for the {@link #usageLimitUsageTimePeriodInMinutes}.
356      * A value of zero indicate unlimited time usage.
357      */
358     private long mUsageLimitTimeLimitInMinutes = Long.MIN_VALUE;
359     /**
360      * @hide
361      */
setUsageLimitTimeLimitInMinutes(long usageLimitTimeLimitInMinutes)362     public void setUsageLimitTimeLimitInMinutes(long usageLimitTimeLimitInMinutes) {
363         mUsageLimitTimeLimitInMinutes = usageLimitTimeLimitInMinutes;
364     }
365     /**
366      * @hide
367      */
getUsageLimitTimeLimitInMinutes()368     public long getUsageLimitTimeLimitInMinutes() {
369         return mUsageLimitTimeLimitInMinutes;
370     }
371 
372     /**
373      * The map of OSU service provider names whose each element is presented in different
374      * languages for the service provider, which is used for finding a matching
375      * PasspointConfiguration with a given service provider name.
376      */
377     private Map<String, String> mServiceFriendlyNames = null;
378 
379     /**
380      * @hide
381      */
setServiceFriendlyNames(Map<String, String> serviceFriendlyNames)382     public void setServiceFriendlyNames(Map<String, String> serviceFriendlyNames) {
383         mServiceFriendlyNames = serviceFriendlyNames;
384     }
385 
386     /**
387      * @hide
388      */
getServiceFriendlyNames()389     public Map<String, String> getServiceFriendlyNames() {
390         return mServiceFriendlyNames;
391     }
392 
393     /**
394      * Return the friendly Name for current language from the list of friendly names of OSU
395      * provider.
396      * The string matching the default locale will be returned if it is found, otherwise the
397      * first string in the list will be returned.  A null will be returned if the list is empty.
398      *
399      * @return String matching the default locale, null otherwise
400      * @hide
401      */
getServiceFriendlyName()402     public String getServiceFriendlyName() {
403         if (mServiceFriendlyNames == null || mServiceFriendlyNames.isEmpty()) return null;
404         String lang = Locale.getDefault().getLanguage();
405         String friendlyName = mServiceFriendlyNames.get(lang);
406         if (friendlyName != null) {
407             return friendlyName;
408         }
409         friendlyName = mServiceFriendlyNames.get("en");
410         if (friendlyName != null) {
411             return friendlyName;
412         }
413         return mServiceFriendlyNames.get(mServiceFriendlyNames.keySet().stream().findFirst().get());
414     }
415 
416     /**
417      * The carrier ID identifies the operator who provides this network configuration.
418      *    see {@link TelephonyManager#getSimCarrierId()}
419      */
420     private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
421 
422     /**
423      * The subscription ID identifies the SIM card who provides this network configuration.
424      * See {@link SubscriptionInfo#getSubscriptionId()}
425      */
426     private int mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
427 
428     /**
429      * Set the carrier ID associated with current configuration.
430      * @param carrierId {@code mCarrierId}
431      * @hide
432      */
setCarrierId(int carrierId)433     public void setCarrierId(int carrierId) {
434         this.mCarrierId = carrierId;
435     }
436 
437     /**
438      * Get the carrier ID associated with current configuration.
439      * @return {@code mCarrierId}
440      * @hide
441      */
getCarrierId()442     public int getCarrierId() {
443         return mCarrierId;
444     }
445 
446     /**
447      * Set the subscription ID associated with current configuration.
448      * @param subscriptionId {@code mSubscriptionId}
449      * @hide
450      */
setSubscriptionId(int subscriptionId)451     public void setSubscriptionId(int subscriptionId) {
452         this.mSubscriptionId = subscriptionId;
453     }
454 
455     /**
456      * Get the carrier ID associated with current configuration.
457      * @return {@code mSubscriptionId}
458      * @hide
459      */
getSubscriptionId()460     public int getSubscriptionId() {
461         return mSubscriptionId;
462     }
463 
464     /**
465      * The auto-join configuration specifies whether or not the Passpoint Configuration is
466      * considered for auto-connection. If true then yes, if false then it isn't considered as part
467      * of auto-connection - but can still be manually connected to.
468      */
469     private boolean mIsAutojoinEnabled = true;
470 
471     /**
472      * The mac randomization setting specifies whether a randomized or device MAC address will
473      * be used to connect to the passpoint network. If true, a randomized MAC will be used.
474      * Otherwise, the device MAC address will be used.
475      */
476     private boolean mIsMacRandomizationEnabled = true;
477 
478     /**
479      * Whether this passpoint configuration should use enhanced MAC randomization.
480      */
481     private boolean mIsEnhancedMacRandomizationEnabled = false;
482 
483 
484     /**
485      * Indicate whether the network is oem paid or not. Networks are considered oem paid
486      * if the corresponding connection is only available to system apps.
487      * @hide
488      */
489     private boolean mIsOemPaid;
490 
491     /**
492      * Indicate whether the network is oem private or not. Networks are considered oem private
493      * if the corresponding connection is only available to system apps.
494      * @hide
495      */
496     private boolean mIsOemPrivate;
497 
498     /**
499      * Indicate whether or not the network is a carrier merged network.
500      * @hide
501      */
502     private boolean mIsCarrierMerged;
503 
504     /**
505      * Indicates if the end user has expressed an explicit opinion about the
506      * meteredness of this network, such as through the Settings app.
507      * This value is one of {@link #METERED_OVERRIDE_NONE}, {@link #METERED_OVERRIDE_METERED},
508      * or {@link #METERED_OVERRIDE_NOT_METERED}.
509      * <p>
510      * This should always override any values from {@link WifiInfo#getMeteredHint()}.
511      *
512      * By default this field is set to {@link #METERED_OVERRIDE_NONE}.
513      */
514     private int mMeteredOverride = METERED_OVERRIDE_NONE;
515 
516     private String mDecoratedIdentityPrefix;
517 
518     /**
519      * Configures the auto-association status of this Passpoint configuration. A value of true
520      * indicates that the configuration will be considered for auto-connection, a value of false
521      * indicates that only manual connection will work - the framework will not auto-associate to
522      * this Passpoint network.
523      *
524      * @param autojoinEnabled true to be considered for framework auto-connection, false otherwise.
525      * @hide
526      */
setAutojoinEnabled(boolean autojoinEnabled)527     public void setAutojoinEnabled(boolean autojoinEnabled) {
528         mIsAutojoinEnabled = autojoinEnabled;
529     }
530 
531     /**
532      * Configures the MAC randomization setting for this Passpoint configuration.
533      * If set to true, the framework will use a randomized MAC address to connect to this Passpoint
534      * network. Otherwise, the framework will use the device MAC address.
535      *
536      * @param enabled true to use randomized MAC address, false to use device MAC address.
537      * @hide
538      */
setMacRandomizationEnabled(boolean enabled)539     public void setMacRandomizationEnabled(boolean enabled) {
540         mIsMacRandomizationEnabled = enabled;
541     }
542 
543     /**
544      * This setting is only applicable if MAC randomization is enabled.
545      * If set to true, the framework will periodically generate new MAC addresses for new
546      * connections.
547      * If set to false (the default), the framework will use the same locally generated MAC address
548      * for connections to this passpoint configuration.
549      * @param enabled true to use enhanced MAC randomization, false to use persistent MAC
550      *                randomization.
551      * @hide
552      */
setEnhancedMacRandomizationEnabled(boolean enabled)553     public void setEnhancedMacRandomizationEnabled(boolean enabled) {
554         mIsEnhancedMacRandomizationEnabled = enabled;
555     }
556 
557     /**
558      * Sets the metered override setting for this Passpoint configuration.
559      *
560      * @param meteredOverride One of the values in {@link MeteredOverride}
561      * @hide
562      */
setMeteredOverride(@eteredOverride int meteredOverride)563     public void setMeteredOverride(@MeteredOverride int meteredOverride) {
564         mMeteredOverride = meteredOverride;
565     }
566 
567     /**
568      * Indicates whether the Passpoint configuration may be auto-connected to by the framework. A
569      * value of true indicates that auto-connection can happen, a value of false indicates that it
570      * cannot. However, even when auto-connection is not possible manual connection by the user is
571      * possible.
572      *
573      * @return the auto-join configuration: true for auto-connection (or join) enabled, false
574      * otherwise.
575      * @hide
576      */
577     @SystemApi
isAutojoinEnabled()578     public boolean isAutojoinEnabled() {
579         return mIsAutojoinEnabled;
580     }
581 
582     /**
583      * Indicates whether the user chose this configuration to be treated as metered or not.
584      *
585      * @return One of the values in {@link MeteredOverride}
586      * @hide
587      */
588     @SystemApi
589     @MeteredOverride
getMeteredOverride()590     public int getMeteredOverride() {
591         return mMeteredOverride;
592     }
593 
594     /**
595      * Indicates whether a randomized MAC address or device MAC address will be used for
596      * connections to this Passpoint network. If true, a randomized MAC address will be used.
597      * Otherwise, the device MAC address will be used.
598      *
599      * @return true for MAC randomization enabled. False for disabled.
600      * @hide
601      */
602     @SystemApi
isMacRandomizationEnabled()603     public boolean isMacRandomizationEnabled() {
604         return mIsMacRandomizationEnabled;
605     }
606 
607     /**
608      * When MAC randomization is enabled, this indicates whether enhanced MAC randomization or
609      * persistent MAC randomization will be used for connections to this Passpoint network.
610      * If true, the MAC address used for connections will periodically change. Otherwise, the same
611      * locally generated MAC will be used for all connections to this passpoint configuration.
612      *
613      * @return true for enhanced MAC randomization enabled. False for disabled.
614      * @hide
615      */
isEnhancedMacRandomizationEnabled()616     public boolean isEnhancedMacRandomizationEnabled() {
617         return mIsEnhancedMacRandomizationEnabled;
618     }
619 
620     /**
621      * Set whether the network is oem paid or not.
622      * @hide
623      */
setOemPaid(boolean isOemPaid)624     public void setOemPaid(boolean isOemPaid) {
625         mIsOemPaid = isOemPaid;
626     }
627 
628     /**
629      * Get whether the network is oem paid or not.
630      * @hide
631      */
isOemPaid()632     public boolean isOemPaid() {
633         return mIsOemPaid;
634     }
635 
636     /**
637      * Set whether the network is oem private or not.
638      * @hide
639      */
setOemPrivate(boolean isOemPrivate)640     public void setOemPrivate(boolean isOemPrivate) {
641         mIsOemPrivate = isOemPrivate;
642     }
643 
644     /**
645      * Get whether the network is oem private or not.
646      * @hide
647      */
isOemPrivate()648     public boolean isOemPrivate() {
649         return mIsOemPrivate;
650     }
651 
652     /**
653      * Set whether the network is carrier merged or not.
654      * @hide
655      */
setCarrierMerged(boolean isCarrierMerged)656     public void setCarrierMerged(boolean isCarrierMerged) {
657         mIsCarrierMerged = isCarrierMerged;
658     }
659 
660     /**
661      * Get whether the network is carrier merged or not.
662      * @hide
663      */
isCarrierMerged()664     public boolean isCarrierMerged() {
665         return mIsCarrierMerged;
666     }
667 
668     /**
669      * Constructor for creating PasspointConfiguration with default values.
670      */
PasspointConfiguration()671     public PasspointConfiguration() {}
672 
673     /**
674      * Copy constructor.
675      *
676      * @param source The source to copy from
677      */
PasspointConfiguration(PasspointConfiguration source)678     public PasspointConfiguration(PasspointConfiguration source) {
679         if (source == null) {
680             return;
681         }
682 
683         if (source.mHomeSp != null) {
684             mHomeSp = new HomeSp(source.mHomeSp);
685         }
686         if (source.mCredential != null) {
687             mCredential = new Credential(source.mCredential);
688         }
689         if (source.mPolicy != null) {
690             mPolicy = new Policy(source.mPolicy);
691         }
692         if (source.mTrustRootCertList != null) {
693             mTrustRootCertList = Collections.unmodifiableMap(source.mTrustRootCertList);
694         }
695         if (source.mSubscriptionUpdate != null) {
696             mSubscriptionUpdate = new UpdateParameter(source.mSubscriptionUpdate);
697         }
698         mUpdateIdentifier = source.mUpdateIdentifier;
699         mCredentialPriority = source.mCredentialPriority;
700         mSubscriptionCreationTimeInMillis = source.mSubscriptionCreationTimeInMillis;
701         mSubscriptionExpirationTimeMillis = source.mSubscriptionExpirationTimeMillis;
702         mSubscriptionType = source.mSubscriptionType;
703         mUsageLimitDataLimit = source.mUsageLimitDataLimit;
704         mUsageLimitStartTimeInMillis = source.mUsageLimitStartTimeInMillis;
705         mUsageLimitTimeLimitInMinutes = source.mUsageLimitTimeLimitInMinutes;
706         mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes;
707         mServiceFriendlyNames = source.mServiceFriendlyNames;
708         mAaaServerTrustedNames = source.mAaaServerTrustedNames;
709         mCarrierId = source.mCarrierId;
710         mSubscriptionId = source.mSubscriptionId;
711         mIsAutojoinEnabled = source.mIsAutojoinEnabled;
712         mIsMacRandomizationEnabled = source.mIsMacRandomizationEnabled;
713         mIsEnhancedMacRandomizationEnabled = source.mIsEnhancedMacRandomizationEnabled;
714         mMeteredOverride = source.mMeteredOverride;
715         mIsCarrierMerged = source.mIsCarrierMerged;
716         mIsOemPaid = source.mIsOemPaid;
717         mIsOemPrivate = source.mIsOemPrivate;
718         mDecoratedIdentityPrefix = source.mDecoratedIdentityPrefix;
719     }
720 
721     @Override
describeContents()722     public int describeContents() {
723         return 0;
724     }
725 
726     @Override
writeToParcel(Parcel dest, int flags)727     public void writeToParcel(Parcel dest, int flags) {
728         dest.writeParcelable(mHomeSp, flags);
729         dest.writeParcelable(mCredential, flags);
730         dest.writeParcelable(mPolicy, flags);
731         dest.writeParcelable(mSubscriptionUpdate, flags);
732         writeTrustRootCerts(dest, mTrustRootCertList);
733         dest.writeInt(mUpdateIdentifier);
734         dest.writeInt(mCredentialPriority);
735         dest.writeLong(mSubscriptionCreationTimeInMillis);
736         dest.writeLong(mSubscriptionExpirationTimeMillis);
737         dest.writeString(mSubscriptionType);
738         dest.writeLong(mUsageLimitUsageTimePeriodInMinutes);
739         dest.writeLong(mUsageLimitStartTimeInMillis);
740         dest.writeLong(mUsageLimitDataLimit);
741         dest.writeLong(mUsageLimitTimeLimitInMinutes);
742         dest.writeStringArray(mAaaServerTrustedNames);
743         Bundle bundle = new Bundle();
744         bundle.putSerializable("serviceFriendlyNames",
745                 (HashMap<String, String>) mServiceFriendlyNames);
746         dest.writeBundle(bundle);
747         dest.writeInt(mCarrierId);
748         dest.writeBoolean(mIsAutojoinEnabled);
749         dest.writeBoolean(mIsMacRandomizationEnabled);
750         dest.writeBoolean(mIsEnhancedMacRandomizationEnabled);
751         dest.writeInt(mMeteredOverride);
752         dest.writeInt(mSubscriptionId);
753         dest.writeBoolean(mIsCarrierMerged);
754         dest.writeBoolean(mIsOemPaid);
755         dest.writeBoolean(mIsOemPrivate);
756         dest.writeString(mDecoratedIdentityPrefix);
757     }
758 
759     @Override
equals(Object thatObject)760     public boolean equals(Object thatObject) {
761         if (this == thatObject) {
762             return true;
763         }
764         if (!(thatObject instanceof PasspointConfiguration)) {
765             return false;
766         }
767         PasspointConfiguration that = (PasspointConfiguration) thatObject;
768         return (mHomeSp == null ? that.mHomeSp == null : mHomeSp.equals(that.mHomeSp))
769                 && (mAaaServerTrustedNames == null ? that.mAaaServerTrustedNames == null
770                 : Arrays.equals(mAaaServerTrustedNames, that.mAaaServerTrustedNames))
771                 && (mCredential == null ? that.mCredential == null
772                 : mCredential.equals(that.mCredential))
773                 && (mPolicy == null ? that.mPolicy == null : mPolicy.equals(that.mPolicy))
774                 && (mSubscriptionUpdate == null ? that.mSubscriptionUpdate == null
775                 : mSubscriptionUpdate.equals(that.mSubscriptionUpdate))
776                 && isTrustRootCertListEquals(mTrustRootCertList, that.mTrustRootCertList)
777                 && mUpdateIdentifier == that.mUpdateIdentifier
778                 && mCredentialPriority == that.mCredentialPriority
779                 && mSubscriptionCreationTimeInMillis == that.mSubscriptionCreationTimeInMillis
780                 && mSubscriptionExpirationTimeMillis == that.mSubscriptionExpirationTimeMillis
781                 && TextUtils.equals(mSubscriptionType, that.mSubscriptionType)
782                 && mUsageLimitUsageTimePeriodInMinutes == that.mUsageLimitUsageTimePeriodInMinutes
783                 && mUsageLimitStartTimeInMillis == that.mUsageLimitStartTimeInMillis
784                 && mUsageLimitDataLimit == that.mUsageLimitDataLimit
785                 && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes
786                 && mCarrierId == that.mCarrierId
787                 && mSubscriptionId == that.mSubscriptionId
788                 && mIsOemPrivate == that.mIsOemPrivate
789                 && mIsOemPaid == that.mIsOemPaid
790                 && mIsCarrierMerged == that.mIsCarrierMerged
791                 && mIsAutojoinEnabled == that.mIsAutojoinEnabled
792                 && mIsMacRandomizationEnabled == that.mIsMacRandomizationEnabled
793                 && mIsEnhancedMacRandomizationEnabled == that.mIsEnhancedMacRandomizationEnabled
794                 && mMeteredOverride == that.mMeteredOverride
795                 && (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null
796                 : mServiceFriendlyNames.equals(that.mServiceFriendlyNames))
797                 && Objects.equals(mDecoratedIdentityPrefix, that.mDecoratedIdentityPrefix);
798     }
799 
800     @Override
hashCode()801     public int hashCode() {
802         return Objects.hash(mHomeSp, mCredential, mPolicy, mSubscriptionUpdate, mTrustRootCertList,
803                 mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis,
804                 mSubscriptionExpirationTimeMillis, mUsageLimitUsageTimePeriodInMinutes,
805                 mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes,
806                 mServiceFriendlyNames, mCarrierId, mIsAutojoinEnabled, mIsMacRandomizationEnabled,
807                 mIsEnhancedMacRandomizationEnabled, mMeteredOverride, mSubscriptionId,
808                 mIsCarrierMerged, mIsOemPaid, mIsOemPrivate, mDecoratedIdentityPrefix);
809     }
810 
811     @Override
toString()812     public String toString() {
813         StringBuilder builder = new StringBuilder();
814         builder.append("UpdateIdentifier: ").append(mUpdateIdentifier).append("\n");
815         builder.append("CredentialPriority: ").append(mCredentialPriority).append("\n");
816         builder.append("SubscriptionCreationTime: ").append(
817                 mSubscriptionCreationTimeInMillis != Long.MIN_VALUE
818                 ? new Date(mSubscriptionCreationTimeInMillis) : "Not specified").append("\n");
819         builder.append("SubscriptionExpirationTime: ").append(
820                 mSubscriptionExpirationTimeMillis != Long.MIN_VALUE
821                 ? new Date(mSubscriptionExpirationTimeMillis) : "Not specified").append("\n");
822         builder.append("UsageLimitStartTime: ").append(mUsageLimitStartTimeInMillis != Long.MIN_VALUE
823                 ? new Date(mUsageLimitStartTimeInMillis) : "Not specified").append("\n");
824         builder.append("UsageTimePeriod: ").append(mUsageLimitUsageTimePeriodInMinutes)
825                 .append("\n");
826         builder.append("UsageLimitDataLimit: ").append(mUsageLimitDataLimit).append("\n");
827         builder.append("UsageLimitTimeLimit: ").append(mUsageLimitTimeLimitInMinutes).append("\n");
828         builder.append("Provisioned by a subscription server: ")
829                 .append(isOsuProvisioned() ? "Yes" : "No").append("\n");
830         if (mHomeSp != null) {
831             builder.append("HomeSP Begin ---\n");
832             builder.append(mHomeSp);
833             builder.append("HomeSP End ---\n");
834         }
835         if (mCredential != null) {
836             builder.append("Credential Begin ---\n");
837             builder.append(mCredential);
838             builder.append("Credential End ---\n");
839         }
840         if (mPolicy != null) {
841             builder.append("Policy Begin ---\n");
842             builder.append(mPolicy);
843             builder.append("Policy End ---\n");
844         }
845         if (mSubscriptionUpdate != null) {
846             builder.append("SubscriptionUpdate Begin ---\n");
847             builder.append(mSubscriptionUpdate);
848             builder.append("SubscriptionUpdate End ---\n");
849         }
850         if (mTrustRootCertList != null) {
851             builder.append("TrustRootCertServers: ").append(mTrustRootCertList.keySet())
852                     .append("\n");
853         }
854         if (mAaaServerTrustedNames != null) {
855             builder.append("AAAServerTrustedNames: ")
856                     .append(String.join(";", mAaaServerTrustedNames)).append("\n");
857         }
858         if (mServiceFriendlyNames != null) {
859             builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames);
860         }
861         builder.append("CarrierId:" + mCarrierId);
862         builder.append("SubscriptionId:" + mSubscriptionId);
863         builder.append("IsAutojoinEnabled:" + mIsAutojoinEnabled);
864         builder.append("mIsMacRandomizationEnabled:" + mIsMacRandomizationEnabled);
865         builder.append("mIsEnhancedMacRandomizationEnabled:" + mIsEnhancedMacRandomizationEnabled);
866         builder.append("mMeteredOverride:" + mMeteredOverride);
867         builder.append("mIsCarrierMerged:" + mIsCarrierMerged);
868         builder.append("mIsOemPaid:" + mIsOemPaid);
869         builder.append("mIsOemPrivate:" + mIsOemPrivate);
870         builder.append("mDecoratedUsernamePrefix:" + mDecoratedIdentityPrefix);
871         return builder.toString();
872     }
873 
874     /**
875      * Validate the R1 configuration data.
876      *
877      * @return true on success or false on failure
878      * @hide
879      */
validate()880     public boolean validate() {
881         // Optional: PerProviderSubscription/<X+>/SubscriptionUpdate
882         if (mSubscriptionUpdate != null && !mSubscriptionUpdate.validate()) {
883             return false;
884         }
885         return validateForCommonR1andR2();
886     }
887 
888     /**
889      * Validate the R2 configuration data.
890      *
891      * @return true on success or false on failure
892      * @hide
893      */
validateForR2()894     public boolean validateForR2() {
895         // Required: PerProviderSubscription/UpdateIdentifier
896         if (mUpdateIdentifier == Integer.MIN_VALUE) {
897             return false;
898         }
899 
900         // Required: PerProviderSubscription/<X+>/SubscriptionUpdate
901         if (mSubscriptionUpdate == null || !mSubscriptionUpdate.validate()) {
902             return false;
903         }
904         return validateForCommonR1andR2();
905     }
906 
validateForCommonR1andR2()907     private boolean validateForCommonR1andR2() {
908         // Required: PerProviderSubscription/<X+>/HomeSP
909         if (mHomeSp == null || !mHomeSp.validate()) {
910             return false;
911         }
912 
913         // Required: PerProviderSubscription/<X+>/Credential
914         if (mCredential == null || !mCredential.validate()) {
915             return false;
916         }
917 
918         // Optional: PerProviderSubscription/<X+>/Policy
919         if (mPolicy != null && !mPolicy.validate()) {
920             return false;
921         }
922 
923         if (mTrustRootCertList != null) {
924             for (Map.Entry<String, byte[]> entry : mTrustRootCertList.entrySet()) {
925                 String url = entry.getKey();
926                 byte[] certFingerprint = entry.getValue();
927                 if (TextUtils.isEmpty(url)) {
928                     Log.d(TAG, "Empty URL");
929                     return false;
930                 }
931                 if (url.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) {
932                     Log.d(TAG, "URL bytes exceeded the max: "
933                             + url.getBytes(StandardCharsets.UTF_8).length);
934                     return false;
935                 }
936 
937                 if (certFingerprint == null) {
938                     Log.d(TAG, "Fingerprint not specified");
939                     return false;
940                 }
941                 if (certFingerprint.length != CERTIFICATE_SHA256_BYTES) {
942                     Log.d(TAG, "Incorrect size of trust root certificate SHA-256 fingerprint: "
943                             + certFingerprint.length);
944                     return false;
945                 }
946             }
947         }
948         return true;
949     }
950 
951     public static final @android.annotation.NonNull Creator<PasspointConfiguration> CREATOR =
952         new Creator<PasspointConfiguration>() {
953             @Override
954             public PasspointConfiguration createFromParcel(Parcel in) {
955                 PasspointConfiguration config = new PasspointConfiguration();
956                 config.setHomeSp(in.readParcelable(null));
957                 config.setCredential(in.readParcelable(null));
958                 config.setPolicy(in.readParcelable(null));
959                 config.setSubscriptionUpdate(in.readParcelable(null));
960                 config.setTrustRootCertList(readTrustRootCerts(in));
961                 config.setUpdateIdentifier(in.readInt());
962                 config.setCredentialPriority(in.readInt());
963                 config.setSubscriptionCreationTimeInMillis(in.readLong());
964                 config.setSubscriptionExpirationTimeInMillis(in.readLong());
965                 config.setSubscriptionType(in.readString());
966                 config.setUsageLimitUsageTimePeriodInMinutes(in.readLong());
967                 config.setUsageLimitStartTimeInMillis(in.readLong());
968                 config.setUsageLimitDataLimit(in.readLong());
969                 config.setUsageLimitTimeLimitInMinutes(in.readLong());
970                 config.setAaaServerTrustedNames(in.createStringArray());
971                 Bundle bundle = in.readBundle();
972                 Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable(
973                         "serviceFriendlyNames");
974                 config.setServiceFriendlyNames(friendlyNamesMap);
975                 config.mCarrierId = in.readInt();
976                 config.mIsAutojoinEnabled = in.readBoolean();
977                 config.mIsMacRandomizationEnabled = in.readBoolean();
978                 config.mIsEnhancedMacRandomizationEnabled = in.readBoolean();
979                 config.mMeteredOverride = in.readInt();
980                 config.mSubscriptionId = in.readInt();
981                 config.mIsCarrierMerged = in.readBoolean();
982                 config.mIsOemPaid = in.readBoolean();
983                 config.mIsOemPrivate = in.readBoolean();
984                 config.mDecoratedIdentityPrefix = in.readString();
985 
986                 return config;
987             }
988 
989             @Override
990             public PasspointConfiguration[] newArray(int size) {
991                 return new PasspointConfiguration[size];
992             }
993 
994             /**
995              * Helper function for reading trust root certificate info list from a Parcel.
996              *
997              * @param in The Parcel to read from
998              * @return The list of trust root certificate URL with the corresponding certificate
999              *         fingerprint
1000              */
1001             private Map<String, byte[]> readTrustRootCerts(Parcel in) {
1002                 int size = in.readInt();
1003                 if (size == NULL_VALUE) {
1004                     return null;
1005                 }
1006                 Map<String, byte[]> trustRootCerts = new HashMap<>(size);
1007                 for (int i = 0; i < size; i++) {
1008                     String key = in.readString();
1009                     byte[] value = in.createByteArray();
1010                     trustRootCerts.put(key, value);
1011                 }
1012                 return trustRootCerts;
1013             }
1014         };
1015 
1016     /**
1017      * Helper function for writing trust root certificate information list.
1018      *
1019      * @param dest The Parcel to write to
1020      * @param trustRootCerts The list of trust root certificate URL with the corresponding
1021      *                       certificate fingerprint
1022      */
writeTrustRootCerts(Parcel dest, Map<String, byte[]> trustRootCerts)1023     private static void writeTrustRootCerts(Parcel dest, Map<String, byte[]> trustRootCerts) {
1024         if (trustRootCerts == null) {
1025             dest.writeInt(NULL_VALUE);
1026             return;
1027         }
1028         dest.writeInt(trustRootCerts.size());
1029         for (Map.Entry<String, byte[]> entry : trustRootCerts.entrySet()) {
1030             dest.writeString(entry.getKey());
1031             dest.writeByteArray(entry.getValue());
1032         }
1033     }
1034 
1035     /**
1036      * Helper function for comparing two trust root certificate list.  Cannot use Map#equals
1037      * method since the value type (byte[]) doesn't override equals method.
1038      *
1039      * @param list1 The first trust root certificate list
1040      * @param list2 The second trust root certificate list
1041      * @return true if the two list are equal
1042      */
isTrustRootCertListEquals(Map<String, byte[]> list1, Map<String, byte[]> list2)1043     private static boolean isTrustRootCertListEquals(Map<String, byte[]> list1,
1044             Map<String, byte[]> list2) {
1045         if (list1 == null || list2 == null) {
1046             return list1 == list2;
1047         }
1048         if (list1.size() != list2.size()) {
1049             return false;
1050         }
1051         for (Map.Entry<String, byte[]> entry : list1.entrySet()) {
1052             if (!Arrays.equals(entry.getValue(), list2.get(entry.getKey()))) {
1053                 return false;
1054             }
1055         }
1056         return true;
1057     }
1058 
1059     /**
1060      * Indicates if the Passpoint Configuration was provisioned by a subscription (OSU) server,
1061      * which means that it's an R2 (or R3) profile.
1062      *
1063      * @return true if the Passpoint Configuration was provisioned by a subscription server.
1064      */
isOsuProvisioned()1065     public boolean isOsuProvisioned() {
1066         return getUpdateIdentifier() != Integer.MIN_VALUE;
1067     }
1068 
1069     /**
1070      * Get a unique identifier for a PasspointConfiguration object. The identifier depends on the
1071      * configuration that identify the service provider under the HomeSp subtree, and on the
1072      * credential configuration under the Credential subtree.
1073      * The method throws an {@link IllegalStateException} if the configuration under HomeSp subtree
1074      * or the configuration under Credential subtree are not initialized.
1075      *
1076      * @return A unique identifier
1077      */
getUniqueId()1078     public @NonNull String getUniqueId() {
1079         if (mCredential == null || mHomeSp == null || TextUtils.isEmpty(mHomeSp.getFqdn())) {
1080             throw new IllegalStateException("Credential or HomeSP are not initialized");
1081         }
1082 
1083         StringBuilder sb = new StringBuilder();
1084         sb.append(String.format("%s_%x%x", mHomeSp.getFqdn(), mHomeSp.getUniqueId(),
1085                 mCredential.getUniqueId()));
1086         return sb.toString();
1087     }
1088 
1089     /**
1090      * Set a prefix for a decorated identity as per RFC 7542.
1091      * This prefix must contain a list of realms (could be a list of 1) delimited by a '!'
1092      * character. e.g. homerealm.example.org! or proxyrealm.example.net!homerealm.example.org!
1093      * A prefix of "homerealm.example.org!" will generate a decorated identity that
1094      * looks like: homerealm.example.org!user@otherrealm.example.net
1095      * Calling with a null parameter will clear the decorated prefix.
1096      * Note: Caller must verify that the device supports this feature by calling
1097      * {@link WifiManager#isDecoratedIdentitySupported()}
1098      *
1099      * @param decoratedIdentityPrefix The prefix to add to the outer/anonymous identity
1100      */
1101     @RequiresApi(Build.VERSION_CODES.S)
setDecoratedIdentityPrefix(@ullable String decoratedIdentityPrefix)1102     public void setDecoratedIdentityPrefix(@Nullable String decoratedIdentityPrefix) {
1103         if (!SdkLevel.isAtLeastS()) {
1104             throw new UnsupportedOperationException();
1105         }
1106         if (!TextUtils.isEmpty(decoratedIdentityPrefix) && !decoratedIdentityPrefix.endsWith("!")) {
1107             throw new IllegalArgumentException(
1108                     "Decorated identity prefix must be delimited by '!'");
1109         }
1110         mDecoratedIdentityPrefix = decoratedIdentityPrefix;
1111     }
1112 
1113     /**
1114      * Get the decorated identity prefix.
1115      *
1116      * @return The decorated identity prefix
1117      */
1118     @RequiresApi(Build.VERSION_CODES.S)
getDecoratedIdentityPrefix()1119     public @Nullable String getDecoratedIdentityPrefix() {
1120         if (!SdkLevel.isAtLeastS()) {
1121             throw new UnsupportedOperationException();
1122         }
1123         return mDecoratedIdentityPrefix;
1124     }
1125 }
1126