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 android.net.wifi.hotspot2.pps.Credential;
20 import android.net.wifi.hotspot2.pps.HomeSp;
21 import android.net.wifi.hotspot2.pps.Policy;
22 import android.net.wifi.hotspot2.pps.UpdateParameter;
23 import android.os.Bundle;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.text.TextUtils;
27 import android.util.Log;
28 
29 import java.nio.charset.StandardCharsets;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.Date;
33 import java.util.HashMap;
34 import java.util.Locale;
35 import java.util.Map;
36 import java.util.Objects;
37 
38 /**
39  * Class representing Passpoint configuration.  This contains configurations specified in
40  * PerProviderSubscription (PPS) Management Object (MO) tree.
41  *
42  * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
43  * Release 2 Technical Specification.
44  */
45 public final class PasspointConfiguration implements Parcelable {
46     private static final String TAG = "PasspointConfiguration";
47 
48     /**
49      * Number of bytes for certificate SHA-256 fingerprint byte array.
50      */
51     private static final int CERTIFICATE_SHA256_BYTES = 32;
52 
53     /**
54      * Maximum bytes for URL string.
55      */
56     private static final int MAX_URL_BYTES = 1023;
57 
58     /**
59      * Integer value used for indicating null value in the Parcel.
60      */
61     private static final int NULL_VALUE = -1;
62 
63     /**
64      * Configurations under HomeSp subtree.
65      */
66     private HomeSp mHomeSp = null;
67     /**
68      * Set the Home SP (Service Provider) information.
69      *
70      * @param homeSp The Home SP information to set to
71      */
setHomeSp(HomeSp homeSp)72     public void setHomeSp(HomeSp homeSp) { mHomeSp = homeSp; }
73     /**
74      * Get the Home SP (Service Provider) information.
75      *
76      * @return Home SP information
77      */
getHomeSp()78     public HomeSp getHomeSp() { return mHomeSp; }
79 
80     /**
81      * Configurations under Credential subtree.
82      */
83     private Credential mCredential = null;
84     /**
85      * Set the credential information.
86      *
87      * @param credential The credential information to set to
88      */
setCredential(Credential credential)89     public void setCredential(Credential credential) {
90         mCredential = credential;
91     }
92     /**
93      * Get the credential information.
94      *
95      * @return credential information
96      */
getCredential()97     public Credential getCredential() {
98         return mCredential;
99     }
100 
101     /**
102      * Configurations under Policy subtree.
103      */
104     private Policy mPolicy = null;
105     /**
106      * @hide
107      */
setPolicy(Policy policy)108     public void setPolicy(Policy policy) {
109         mPolicy = policy;
110     }
111     /**
112      * @hide
113      */
getPolicy()114     public Policy getPolicy() {
115         return mPolicy;
116     }
117 
118     /**
119      * Meta data for performing subscription update.
120      */
121     private UpdateParameter mSubscriptionUpdate = null;
122     /**
123      * @hide
124      */
setSubscriptionUpdate(UpdateParameter subscriptionUpdate)125     public void setSubscriptionUpdate(UpdateParameter subscriptionUpdate) {
126         mSubscriptionUpdate = subscriptionUpdate;
127     }
128     /**
129      * @hide
130      */
getSubscriptionUpdate()131     public UpdateParameter getSubscriptionUpdate() {
132         return mSubscriptionUpdate;
133     }
134 
135     /**
136      * List of HTTPS URL for retrieving trust root certificate and the corresponding SHA-256
137      * fingerprint of the certificate.  The certificates are used for verifying AAA server's
138      * identity during EAP authentication.
139      */
140     private Map<String, byte[]> mTrustRootCertList = null;
141     /**
142      * @hide
143      */
setTrustRootCertList(Map<String, byte[]> trustRootCertList)144     public void setTrustRootCertList(Map<String, byte[]> trustRootCertList) {
145         mTrustRootCertList = trustRootCertList;
146     }
147     /**
148      * @hide
149      */
getTrustRootCertList()150     public Map<String, byte[]> getTrustRootCertList() {
151         return mTrustRootCertList;
152     }
153 
154     /**
155      * Set by the subscription server, updated every time the configuration is updated by
156      * the subscription server.
157      *
158      * Use Integer.MIN_VALUE to indicate unset value.
159      */
160     private int mUpdateIdentifier = Integer.MIN_VALUE;
161     /**
162      * @hide
163      */
setUpdateIdentifier(int updateIdentifier)164     public void setUpdateIdentifier(int updateIdentifier) {
165         mUpdateIdentifier = updateIdentifier;
166     }
167     /**
168      * @hide
169      */
getUpdateIdentifier()170     public int getUpdateIdentifier() {
171         return mUpdateIdentifier;
172     }
173 
174     /**
175      * The priority of the credential.
176      *
177      * Use Integer.MIN_VALUE to indicate unset value.
178      */
179     private int mCredentialPriority = Integer.MIN_VALUE;
180     /**
181      * @hide
182      */
setCredentialPriority(int credentialPriority)183     public void setCredentialPriority(int credentialPriority) {
184         mCredentialPriority = credentialPriority;
185     }
186     /**
187      * @hide
188      */
getCredentialPriority()189     public int getCredentialPriority() {
190         return mCredentialPriority;
191     }
192 
193     /**
194      * The time this subscription is created. 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 mSubscriptionCreationTimeInMillis = Long.MIN_VALUE;
200     /**
201      * @hide
202      */
setSubscriptionCreationTimeInMillis(long subscriptionCreationTimeInMillis)203     public void setSubscriptionCreationTimeInMillis(long subscriptionCreationTimeInMillis) {
204         mSubscriptionCreationTimeInMillis = subscriptionCreationTimeInMillis;
205     }
206     /**
207      * @hide
208      */
getSubscriptionCreationTimeInMillis()209     public long getSubscriptionCreationTimeInMillis() {
210         return mSubscriptionCreationTimeInMillis;
211     }
212 
213     /**
214      * The time this subscription will expire. It is in the format of number
215      * of milliseconds since January 1, 1970, 00:00:00 GMT.
216      *
217      * Use Long.MIN_VALUE to indicate unset value.
218      */
219     private long mSubscriptionExpirationTimeInMillis = Long.MIN_VALUE;
220     /**
221      * @hide
222      */
setSubscriptionExpirationTimeInMillis(long subscriptionExpirationTimeInMillis)223     public void setSubscriptionExpirationTimeInMillis(long subscriptionExpirationTimeInMillis) {
224         mSubscriptionExpirationTimeInMillis = subscriptionExpirationTimeInMillis;
225     }
226     /**
227      * @hide
228      */
getSubscriptionExpirationTimeInMillis()229     public long getSubscriptionExpirationTimeInMillis() {
230         return mSubscriptionExpirationTimeInMillis;
231     }
232 
233     /**
234      * The type of the subscription.  This is defined by the provider and the value is provider
235      * specific.
236      */
237     private String mSubscriptionType = null;
238     /**
239      * @hide
240      */
setSubscriptionType(String subscriptionType)241     public void setSubscriptionType(String subscriptionType) {
242         mSubscriptionType = subscriptionType;
243     }
244     /**
245      * @hide
246      */
getSubscriptionType()247     public String getSubscriptionType() {
248         return mSubscriptionType;
249     }
250 
251     /**
252      * The time period for usage statistics accumulation. A value of zero means that usage
253      * statistics are not accumulated on a periodic basis (e.g., a one-time limit for
254      * “pay as you go” - PAYG service). A non-zero value specifies the usage interval in minutes.
255      */
256     private long mUsageLimitUsageTimePeriodInMinutes = Long.MIN_VALUE;
257     /**
258      * @hide
259      */
setUsageLimitUsageTimePeriodInMinutes(long usageLimitUsageTimePeriodInMinutes)260     public void setUsageLimitUsageTimePeriodInMinutes(long usageLimitUsageTimePeriodInMinutes) {
261         mUsageLimitUsageTimePeriodInMinutes = usageLimitUsageTimePeriodInMinutes;
262     }
263     /**
264      * @hide
265      */
getUsageLimitUsageTimePeriodInMinutes()266     public long getUsageLimitUsageTimePeriodInMinutes() {
267         return mUsageLimitUsageTimePeriodInMinutes;
268     }
269 
270     /**
271      * The time at which usage statistic accumulation  begins.  It is in the format of number
272      * of milliseconds since January 1, 1970, 00:00:00 GMT.
273      *
274      * Use Long.MIN_VALUE to indicate unset value.
275      */
276     private long mUsageLimitStartTimeInMillis = Long.MIN_VALUE;
277     /**
278      * @hide
279      */
setUsageLimitStartTimeInMillis(long usageLimitStartTimeInMillis)280     public void setUsageLimitStartTimeInMillis(long usageLimitStartTimeInMillis) {
281         mUsageLimitStartTimeInMillis = usageLimitStartTimeInMillis;
282     }
283     /**
284      * @hide
285      */
getUsageLimitStartTimeInMillis()286     public long getUsageLimitStartTimeInMillis() {
287         return mUsageLimitStartTimeInMillis;
288     }
289 
290     /**
291      * The cumulative data limit in megabytes for the {@link #usageLimitUsageTimePeriodInMinutes}.
292      * A value of zero indicate unlimited data usage.
293      *
294      * Use Long.MIN_VALUE to indicate unset value.
295      */
296     private long mUsageLimitDataLimit = Long.MIN_VALUE;
297     /**
298      * @hide
299      */
setUsageLimitDataLimit(long usageLimitDataLimit)300     public void setUsageLimitDataLimit(long usageLimitDataLimit) {
301         mUsageLimitDataLimit = usageLimitDataLimit;
302     }
303     /**
304      * @hide
305      */
getUsageLimitDataLimit()306     public long getUsageLimitDataLimit() {
307         return mUsageLimitDataLimit;
308     }
309 
310     /**
311      * The cumulative time limit in minutes for the {@link #usageLimitUsageTimePeriodInMinutes}.
312      * A value of zero indicate unlimited time usage.
313      */
314     private long mUsageLimitTimeLimitInMinutes = Long.MIN_VALUE;
315     /**
316      * @hide
317      */
setUsageLimitTimeLimitInMinutes(long usageLimitTimeLimitInMinutes)318     public void setUsageLimitTimeLimitInMinutes(long usageLimitTimeLimitInMinutes) {
319         mUsageLimitTimeLimitInMinutes = usageLimitTimeLimitInMinutes;
320     }
321     /**
322      * @hide
323      */
getUsageLimitTimeLimitInMinutes()324     public long getUsageLimitTimeLimitInMinutes() {
325         return mUsageLimitTimeLimitInMinutes;
326     }
327 
328     /**
329      * The map of OSU service provider names whose each element is presented in different
330      * languages for the service provider, which is used for finding a matching
331      * PasspointConfiguration with a given service provider name.
332      */
333     private Map<String, String> mServiceFriendlyNames = null;
334 
335     /**
336      * @hide
337      */
setServiceFriendlyNames(Map<String, String> serviceFriendlyNames)338     public void setServiceFriendlyNames(Map<String, String> serviceFriendlyNames) {
339         mServiceFriendlyNames = serviceFriendlyNames;
340     }
341 
342     /**
343      * @hide
344      */
getServiceFriendlyNames()345     public Map<String, String> getServiceFriendlyNames() {
346         return mServiceFriendlyNames;
347     }
348 
349     /**
350      * Return the friendly Name for current language from the list of friendly names of OSU
351      * provider.
352      * The string matching the default locale will be returned if it is found, otherwise the
353      * first string in the list will be returned.  A null will be returned if the list is empty.
354      *
355      * @return String matching the default locale, null otherwise
356      * @hide
357      */
getServiceFriendlyName()358     public String getServiceFriendlyName() {
359         if (mServiceFriendlyNames == null || mServiceFriendlyNames.isEmpty()) return null;
360         String lang = Locale.getDefault().getLanguage();
361         String friendlyName = mServiceFriendlyNames.get(lang);
362         if (friendlyName != null) {
363             return friendlyName;
364         }
365         friendlyName = mServiceFriendlyNames.get("en");
366         if (friendlyName != null) {
367             return friendlyName;
368         }
369         return mServiceFriendlyNames.get(mServiceFriendlyNames.keySet().stream().findFirst().get());
370     }
371 
372     /**
373      * Constructor for creating PasspointConfiguration with default values.
374      */
PasspointConfiguration()375     public PasspointConfiguration() {}
376 
377     /**
378      * Copy constructor.
379      *
380      * @param source The source to copy from
381      */
PasspointConfiguration(PasspointConfiguration source)382     public PasspointConfiguration(PasspointConfiguration source) {
383         if (source == null) {
384             return;
385         }
386 
387         if (source.mHomeSp != null) {
388             mHomeSp = new HomeSp(source.mHomeSp);
389         }
390         if (source.mCredential != null) {
391             mCredential = new Credential(source.mCredential);
392         }
393         if (source.mPolicy != null) {
394             mPolicy = new Policy(source.mPolicy);
395         }
396         if (source.mTrustRootCertList != null) {
397             mTrustRootCertList = Collections.unmodifiableMap(source.mTrustRootCertList);
398         }
399         if (source.mSubscriptionUpdate != null) {
400             mSubscriptionUpdate = new UpdateParameter(source.mSubscriptionUpdate);
401         }
402         mUpdateIdentifier = source.mUpdateIdentifier;
403         mCredentialPriority = source.mCredentialPriority;
404         mSubscriptionCreationTimeInMillis = source.mSubscriptionCreationTimeInMillis;
405         mSubscriptionExpirationTimeInMillis = source.mSubscriptionExpirationTimeInMillis;
406         mSubscriptionType = source.mSubscriptionType;
407         mUsageLimitDataLimit = source.mUsageLimitDataLimit;
408         mUsageLimitStartTimeInMillis = source.mUsageLimitStartTimeInMillis;
409         mUsageLimitTimeLimitInMinutes = source.mUsageLimitTimeLimitInMinutes;
410         mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes;
411         mServiceFriendlyNames = source.mServiceFriendlyNames;
412     }
413 
414     @Override
describeContents()415     public int describeContents() {
416         return 0;
417     }
418 
419     @Override
writeToParcel(Parcel dest, int flags)420     public void writeToParcel(Parcel dest, int flags) {
421         dest.writeParcelable(mHomeSp, flags);
422         dest.writeParcelable(mCredential, flags);
423         dest.writeParcelable(mPolicy, flags);
424         dest.writeParcelable(mSubscriptionUpdate, flags);
425         writeTrustRootCerts(dest, mTrustRootCertList);
426         dest.writeInt(mUpdateIdentifier);
427         dest.writeInt(mCredentialPriority);
428         dest.writeLong(mSubscriptionCreationTimeInMillis);
429         dest.writeLong(mSubscriptionExpirationTimeInMillis);
430         dest.writeString(mSubscriptionType);
431         dest.writeLong(mUsageLimitUsageTimePeriodInMinutes);
432         dest.writeLong(mUsageLimitStartTimeInMillis);
433         dest.writeLong(mUsageLimitDataLimit);
434         dest.writeLong(mUsageLimitTimeLimitInMinutes);
435         Bundle bundle = new Bundle();
436         bundle.putSerializable("serviceFriendlyNames",
437                 (HashMap<String, String>) mServiceFriendlyNames);
438         dest.writeBundle(bundle);
439     }
440 
441     @Override
equals(Object thatObject)442     public boolean equals(Object thatObject) {
443         if (this == thatObject) {
444             return true;
445         }
446         if (!(thatObject instanceof PasspointConfiguration)) {
447             return false;
448         }
449         PasspointConfiguration that = (PasspointConfiguration) thatObject;
450         return (mHomeSp == null ? that.mHomeSp == null : mHomeSp.equals(that.mHomeSp))
451                 && (mCredential == null ? that.mCredential == null
452                 : mCredential.equals(that.mCredential))
453                 && (mPolicy == null ? that.mPolicy == null : mPolicy.equals(that.mPolicy))
454                 && (mSubscriptionUpdate == null ? that.mSubscriptionUpdate == null
455                 : mSubscriptionUpdate.equals(that.mSubscriptionUpdate))
456                 && isTrustRootCertListEquals(mTrustRootCertList, that.mTrustRootCertList)
457                 && mUpdateIdentifier == that.mUpdateIdentifier
458                 && mCredentialPriority == that.mCredentialPriority
459                 && mSubscriptionCreationTimeInMillis == that.mSubscriptionCreationTimeInMillis
460                 && mSubscriptionExpirationTimeInMillis == that.mSubscriptionExpirationTimeInMillis
461                 && TextUtils.equals(mSubscriptionType, that.mSubscriptionType)
462                 && mUsageLimitUsageTimePeriodInMinutes == that.mUsageLimitUsageTimePeriodInMinutes
463                 && mUsageLimitStartTimeInMillis == that.mUsageLimitStartTimeInMillis
464                 && mUsageLimitDataLimit == that.mUsageLimitDataLimit
465                 && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes
466                 && (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null
467                 : mServiceFriendlyNames.equals(that.mServiceFriendlyNames));
468     }
469 
470     @Override
hashCode()471     public int hashCode() {
472         return Objects.hash(mHomeSp, mCredential, mPolicy, mSubscriptionUpdate, mTrustRootCertList,
473                 mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis,
474                 mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes,
475                 mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes,
476                 mServiceFriendlyNames);
477     }
478 
479     @Override
toString()480     public String toString() {
481         StringBuilder builder = new StringBuilder();
482         builder.append("UpdateIdentifier: ").append(mUpdateIdentifier).append("\n");
483         builder.append("CredentialPriority: ").append(mCredentialPriority).append("\n");
484         builder.append("SubscriptionCreationTime: ").append(
485                 mSubscriptionCreationTimeInMillis != Long.MIN_VALUE
486                 ? new Date(mSubscriptionCreationTimeInMillis) : "Not specified").append("\n");
487         builder.append("SubscriptionExpirationTime: ").append(
488                 mSubscriptionExpirationTimeInMillis != Long.MIN_VALUE
489                 ? new Date(mSubscriptionExpirationTimeInMillis) : "Not specified").append("\n");
490         builder.append("UsageLimitStartTime: ").append(mUsageLimitStartTimeInMillis != Long.MIN_VALUE
491                 ? new Date(mUsageLimitStartTimeInMillis) : "Not specified").append("\n");
492         builder.append("UsageTimePeriod: ").append(mUsageLimitUsageTimePeriodInMinutes)
493                 .append("\n");
494         builder.append("UsageLimitDataLimit: ").append(mUsageLimitDataLimit).append("\n");
495         builder.append("UsageLimitTimeLimit: ").append(mUsageLimitTimeLimitInMinutes).append("\n");
496         if (mHomeSp != null) {
497             builder.append("HomeSP Begin ---\n");
498             builder.append(mHomeSp);
499             builder.append("HomeSP End ---\n");
500         }
501         if (mCredential != null) {
502             builder.append("Credential Begin ---\n");
503             builder.append(mCredential);
504             builder.append("Credential End ---\n");
505         }
506         if (mPolicy != null) {
507             builder.append("Policy Begin ---\n");
508             builder.append(mPolicy);
509             builder.append("Policy End ---\n");
510         }
511         if (mSubscriptionUpdate != null) {
512             builder.append("SubscriptionUpdate Begin ---\n");
513             builder.append(mSubscriptionUpdate);
514             builder.append("SubscriptionUpdate End ---\n");
515         }
516         if (mTrustRootCertList != null) {
517             builder.append("TrustRootCertServers: ").append(mTrustRootCertList.keySet())
518                     .append("\n");
519         }
520         if (mServiceFriendlyNames != null) {
521             builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames);
522         }
523         return builder.toString();
524     }
525 
526     /**
527      * Validate the R1 configuration data.
528      *
529      * @return true on success or false on failure
530      * @hide
531      */
validate()532     public boolean validate() {
533         // Optional: PerProviderSubscription/<X+>/SubscriptionUpdate
534         if (mSubscriptionUpdate != null && !mSubscriptionUpdate.validate()) {
535             return false;
536         }
537         return validateForCommonR1andR2(true);
538     }
539 
540     /**
541      * Validate the R2 configuration data.
542      *
543      * @return true on success or false on failure
544      * @hide
545      */
validateForR2()546     public boolean validateForR2() {
547         // Required: PerProviderSubscription/UpdateIdentifier
548         if (mUpdateIdentifier == Integer.MIN_VALUE) {
549             return false;
550         }
551 
552         // Required: PerProviderSubscription/<X+>/SubscriptionUpdate
553         if (mSubscriptionUpdate == null || !mSubscriptionUpdate.validate()) {
554             return false;
555         }
556         return validateForCommonR1andR2(false);
557     }
558 
validateForCommonR1andR2(boolean isR1)559     private boolean validateForCommonR1andR2(boolean isR1) {
560         // Required: PerProviderSubscription/<X+>/HomeSP
561         if (mHomeSp == null || !mHomeSp.validate()) {
562             return false;
563         }
564 
565         // Required: PerProviderSubscription/<X+>/Credential
566         if (mCredential == null || !mCredential.validate(isR1)) {
567             return false;
568         }
569 
570         // Optional: PerProviderSubscription/<X+>/Policy
571         if (mPolicy != null && !mPolicy.validate()) {
572             return false;
573         }
574 
575         if (mTrustRootCertList != null) {
576             for (Map.Entry<String, byte[]> entry : mTrustRootCertList.entrySet()) {
577                 String url = entry.getKey();
578                 byte[] certFingerprint = entry.getValue();
579                 if (TextUtils.isEmpty(url)) {
580                     Log.d(TAG, "Empty URL");
581                     return false;
582                 }
583                 if (url.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) {
584                     Log.d(TAG, "URL bytes exceeded the max: "
585                             + url.getBytes(StandardCharsets.UTF_8).length);
586                     return false;
587                 }
588 
589                 if (certFingerprint == null) {
590                     Log.d(TAG, "Fingerprint not specified");
591                     return false;
592                 }
593                 if (certFingerprint.length != CERTIFICATE_SHA256_BYTES) {
594                     Log.d(TAG, "Incorrect size of trust root certificate SHA-256 fingerprint: "
595                             + certFingerprint.length);
596                     return false;
597                 }
598             }
599         }
600         return true;
601     }
602 
603     public static final @android.annotation.NonNull Creator<PasspointConfiguration> CREATOR =
604         new Creator<PasspointConfiguration>() {
605             @Override
606             public PasspointConfiguration createFromParcel(Parcel in) {
607                 PasspointConfiguration config = new PasspointConfiguration();
608                 config.setHomeSp(in.readParcelable(null));
609                 config.setCredential(in.readParcelable(null));
610                 config.setPolicy(in.readParcelable(null));
611                 config.setSubscriptionUpdate(in.readParcelable(null));
612                 config.setTrustRootCertList(readTrustRootCerts(in));
613                 config.setUpdateIdentifier(in.readInt());
614                 config.setCredentialPriority(in.readInt());
615                 config.setSubscriptionCreationTimeInMillis(in.readLong());
616                 config.setSubscriptionExpirationTimeInMillis(in.readLong());
617                 config.setSubscriptionType(in.readString());
618                 config.setUsageLimitUsageTimePeriodInMinutes(in.readLong());
619                 config.setUsageLimitStartTimeInMillis(in.readLong());
620                 config.setUsageLimitDataLimit(in.readLong());
621                 config.setUsageLimitTimeLimitInMinutes(in.readLong());
622                 Bundle bundle = in.readBundle();
623                 Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable(
624                         "serviceFriendlyNames");
625                 config.setServiceFriendlyNames(friendlyNamesMap);
626                 return config;
627             }
628 
629             @Override
630             public PasspointConfiguration[] newArray(int size) {
631                 return new PasspointConfiguration[size];
632             }
633 
634             /**
635              * Helper function for reading trust root certificate info list from a Parcel.
636              *
637              * @param in The Parcel to read from
638              * @return The list of trust root certificate URL with the corresponding certificate
639              *         fingerprint
640              */
641             private Map<String, byte[]> readTrustRootCerts(Parcel in) {
642                 int size = in.readInt();
643                 if (size == NULL_VALUE) {
644                     return null;
645                 }
646                 Map<String, byte[]> trustRootCerts = new HashMap<>(size);
647                 for (int i = 0; i < size; i++) {
648                     String key = in.readString();
649                     byte[] value = in.createByteArray();
650                     trustRootCerts.put(key, value);
651                 }
652                 return trustRootCerts;
653             }
654         };
655 
656     /**
657      * Helper function for writing trust root certificate information list.
658      *
659      * @param dest The Parcel to write to
660      * @param trustRootCerts The list of trust root certificate URL with the corresponding
661      *                       certificate fingerprint
662      */
writeTrustRootCerts(Parcel dest, Map<String, byte[]> trustRootCerts)663     private static void writeTrustRootCerts(Parcel dest, Map<String, byte[]> trustRootCerts) {
664         if (trustRootCerts == null) {
665             dest.writeInt(NULL_VALUE);
666             return;
667         }
668         dest.writeInt(trustRootCerts.size());
669         for (Map.Entry<String, byte[]> entry : trustRootCerts.entrySet()) {
670             dest.writeString(entry.getKey());
671             dest.writeByteArray(entry.getValue());
672         }
673     }
674 
675     /**
676      * Helper function for comparing two trust root certificate list.  Cannot use Map#equals
677      * method since the value type (byte[]) doesn't override equals method.
678      *
679      * @param list1 The first trust root certificate list
680      * @param list2 The second trust root certificate list
681      * @return true if the two list are equal
682      */
isTrustRootCertListEquals(Map<String, byte[]> list1, Map<String, byte[]> list2)683     private static boolean isTrustRootCertListEquals(Map<String, byte[]> list1,
684             Map<String, byte[]> list2) {
685         if (list1 == null || list2 == null) {
686             return list1 == list2;
687         }
688         if (list1.size() != list2.size()) {
689             return false;
690         }
691         for (Map.Entry<String, byte[]> entry : list1.entrySet()) {
692             if (!Arrays.equals(entry.getValue(), list2.get(entry.getKey()))) {
693                 return false;
694             }
695         }
696         return true;
697     }
698 }
699