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