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