Home | History | Annotate | Download | only in pps
      1 /**
      2  * Copyright (c) 2017, 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.pps;
     18 
     19 import android.os.Parcel;
     20 import android.os.Parcelable;
     21 import android.text.TextUtils;
     22 import android.util.Base64;
     23 import android.util.Log;
     24 
     25 import java.nio.charset.StandardCharsets;
     26 import java.util.Arrays;
     27 import java.util.Objects;
     28 
     29 /**
     30  * Class representing configuration parameters for subscription or policy update in
     31  * PerProviderSubscription (PPS) Management Object (MO) tree.  This is used by both
     32  * PerProviderSubscription/Policy/PolicyUpdate and PerProviderSubscription/SubscriptionUpdate
     33  * subtree.
     34  *
     35  * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
     36  * Release 2 Technical Specification.
     37  *
     38  * @hide
     39  */
     40 public final class UpdateParameter implements Parcelable {
     41     private static final String TAG = "UpdateParameter";
     42 
     43     /**
     44      * Value indicating policy update is not applicable.  Thus, never check with policy server
     45      * for updates.
     46      */
     47     public static final long UPDATE_CHECK_INTERVAL_NEVER = 0xFFFFFFFFL;
     48 
     49     /**
     50      * Valid string for UpdateMethod.
     51      */
     52     public static final String UPDATE_METHOD_OMADM = "OMA-DM-ClientInitiated";
     53     public static final String UPDATE_METHOD_SSP = "SSP-ClientInitiated";
     54 
     55     /**
     56      * Valid string for Restriction.
     57      */
     58     public static final String UPDATE_RESTRICTION_HOMESP = "HomeSP";
     59     public static final String UPDATE_RESTRICTION_ROAMING_PARTNER = "RoamingPartner";
     60     public static final String UPDATE_RESTRICTION_UNRESTRICTED = "Unrestricted";
     61 
     62     /**
     63      * Maximum bytes for URI string.
     64      */
     65     private static final int MAX_URI_BYTES = 1023;
     66 
     67     /**
     68      * Maximum bytes for URI string.
     69      */
     70     private static final int MAX_URL_BYTES = 1023;
     71 
     72     /**
     73      * Maximum bytes for username.
     74      */
     75     private static final int MAX_USERNAME_BYTES = 63;
     76 
     77     /**
     78      * Maximum bytes for password.
     79      */
     80     private static final int MAX_PASSWORD_BYTES = 255;
     81 
     82     /**
     83      * Number of bytes for certificate SHA-256 fingerprint byte array.
     84      */
     85     private static final int CERTIFICATE_SHA256_BYTES = 32;
     86 
     87     /**
     88      * This specifies how often the mobile device shall check with policy server for updates.
     89      *
     90      * Using Long.MIN_VALUE to indicate unset value.
     91      */
     92     private long mUpdateIntervalInMinutes = Long.MIN_VALUE;
     93     public void setUpdateIntervalInMinutes(long updateIntervalInMinutes) {
     94         mUpdateIntervalInMinutes = updateIntervalInMinutes;
     95     }
     96     public long getUpdateIntervalInMinutes() {
     97         return mUpdateIntervalInMinutes;
     98     }
     99 
    100     /**
    101      * The method used to update the policy.  Permitted values are "OMA-DM-ClientInitiated"
    102      * and "SPP-ClientInitiated".
    103      */
    104     private String mUpdateMethod = null;
    105     public void setUpdateMethod(String updateMethod) {
    106         mUpdateMethod = updateMethod;
    107     }
    108     public String getUpdateMethod() {
    109         return mUpdateMethod;
    110     }
    111 
    112     /**
    113      * This specifies the hotspots at which the subscription update is permitted.  Permitted
    114      * values are "HomeSP", "RoamingPartner", or "Unrestricted";
    115      */
    116     private String mRestriction = null;
    117     public void setRestriction(String restriction) {
    118         mRestriction = restriction;
    119     }
    120     public String getRestriction() {
    121         return mRestriction;
    122     }
    123 
    124     /**
    125      * The URI of the update server.
    126      */
    127     private String mServerUri = null;
    128     public void setServerUri(String serverUri) {
    129         mServerUri = serverUri;
    130     }
    131     public String getServerUri() {
    132         return mServerUri;
    133     }
    134 
    135     /**
    136      * Username used to authenticate with the policy server.
    137      */
    138     private String mUsername = null;
    139     public void setUsername(String username) {
    140         mUsername = username;
    141     }
    142     public String getUsername() {
    143         return mUsername;
    144     }
    145 
    146     /**
    147      * Base64 encoded password used to authenticate with the policy server.
    148      */
    149     private String mBase64EncodedPassword = null;
    150     public void setBase64EncodedPassword(String password) {
    151         mBase64EncodedPassword = password;
    152     }
    153     public String getBase64EncodedPassword() {
    154         return mBase64EncodedPassword;
    155     }
    156 
    157     /**
    158      * HTTPS URL for retrieving certificate for trust root.  The trust root is used to validate
    159      * policy server's identity.
    160      */
    161     private String mTrustRootCertUrl = null;
    162     public void setTrustRootCertUrl(String trustRootCertUrl) {
    163         mTrustRootCertUrl = trustRootCertUrl;
    164     }
    165     public String getTrustRootCertUrl() {
    166         return mTrustRootCertUrl;
    167     }
    168 
    169     /**
    170      * SHA-256 fingerprint of the certificate located at {@link #trustRootCertUrl}
    171      */
    172     private byte[] mTrustRootCertSha256Fingerprint = null;
    173     public void setTrustRootCertSha256Fingerprint(byte[] fingerprint) {
    174         mTrustRootCertSha256Fingerprint = fingerprint;
    175     }
    176     public byte[] getTrustRootCertSha256Fingerprint() {
    177         return mTrustRootCertSha256Fingerprint;
    178     }
    179 
    180     /**
    181      * Constructor for creating Policy with default values.
    182      */
    183     public UpdateParameter() {}
    184 
    185     /**
    186      * Copy constructor.
    187      *
    188      * @param source The source to copy from
    189      */
    190     public UpdateParameter(UpdateParameter source) {
    191         if (source == null) {
    192             return;
    193         }
    194         mUpdateIntervalInMinutes = source.mUpdateIntervalInMinutes;
    195         mUpdateMethod = source.mUpdateMethod;
    196         mRestriction = source.mRestriction;
    197         mServerUri = source.mServerUri;
    198         mUsername = source.mUsername;
    199         mBase64EncodedPassword = source.mBase64EncodedPassword;
    200         mTrustRootCertUrl = source.mTrustRootCertUrl;
    201         if (source.mTrustRootCertSha256Fingerprint != null) {
    202             mTrustRootCertSha256Fingerprint = Arrays.copyOf(source.mTrustRootCertSha256Fingerprint,
    203                     source.mTrustRootCertSha256Fingerprint.length);
    204         }
    205     }
    206 
    207     @Override
    208     public int describeContents() {
    209         return 0;
    210     }
    211 
    212     @Override
    213     public void writeToParcel(Parcel dest, int flags) {
    214         dest.writeLong(mUpdateIntervalInMinutes);
    215         dest.writeString(mUpdateMethod);
    216         dest.writeString(mRestriction);
    217         dest.writeString(mServerUri);
    218         dest.writeString(mUsername);
    219         dest.writeString(mBase64EncodedPassword);
    220         dest.writeString(mTrustRootCertUrl);
    221         dest.writeByteArray(mTrustRootCertSha256Fingerprint);
    222     }
    223 
    224     @Override
    225     public boolean equals(Object thatObject) {
    226         if (this == thatObject) {
    227             return true;
    228         }
    229         if (!(thatObject instanceof UpdateParameter)) {
    230             return false;
    231         }
    232         UpdateParameter that = (UpdateParameter) thatObject;
    233 
    234         return mUpdateIntervalInMinutes == that.mUpdateIntervalInMinutes
    235                 && TextUtils.equals(mUpdateMethod, that.mUpdateMethod)
    236                 && TextUtils.equals(mRestriction, that.mRestriction)
    237                 && TextUtils.equals(mServerUri, that.mServerUri)
    238                 && TextUtils.equals(mUsername, that.mUsername)
    239                 && TextUtils.equals(mBase64EncodedPassword, that.mBase64EncodedPassword)
    240                 && TextUtils.equals(mTrustRootCertUrl, that.mTrustRootCertUrl)
    241                 && Arrays.equals(mTrustRootCertSha256Fingerprint,
    242                         that.mTrustRootCertSha256Fingerprint);
    243     }
    244 
    245     @Override
    246     public int hashCode() {
    247         return Objects.hash(mUpdateIntervalInMinutes, mUpdateMethod, mRestriction, mServerUri,
    248                 mUsername, mBase64EncodedPassword, mTrustRootCertUrl,
    249                 mTrustRootCertSha256Fingerprint);
    250     }
    251 
    252     @Override
    253     public String toString() {
    254         StringBuilder builder = new StringBuilder();
    255         builder.append("UpdateInterval: ").append(mUpdateIntervalInMinutes).append("\n");
    256         builder.append("UpdateMethod: ").append(mUpdateMethod).append("\n");
    257         builder.append("Restriction: ").append(mRestriction).append("\n");
    258         builder.append("ServerURI: ").append(mServerUri).append("\n");
    259         builder.append("Username: ").append(mUsername).append("\n");
    260         builder.append("TrustRootCertURL: ").append(mTrustRootCertUrl).append("\n");
    261         return builder.toString();
    262     }
    263 
    264     /**
    265      * Validate UpdateParameter data.
    266      *
    267      * @return true on success
    268      * @hide
    269      */
    270     public boolean validate() {
    271         if (mUpdateIntervalInMinutes == Long.MIN_VALUE) {
    272             Log.d(TAG, "Update interval not specified");
    273             return false;
    274         }
    275         // Update not applicable.
    276         if (mUpdateIntervalInMinutes == UPDATE_CHECK_INTERVAL_NEVER) {
    277             return true;
    278         }
    279 
    280         if (!TextUtils.equals(mUpdateMethod, UPDATE_METHOD_OMADM)
    281                 && !TextUtils.equals(mUpdateMethod, UPDATE_METHOD_SSP)) {
    282             Log.d(TAG, "Unknown update method: " + mUpdateMethod);
    283             return false;
    284         }
    285 
    286         if (!TextUtils.equals(mRestriction, UPDATE_RESTRICTION_HOMESP)
    287                 && !TextUtils.equals(mRestriction, UPDATE_RESTRICTION_ROAMING_PARTNER)
    288                 && !TextUtils.equals(mRestriction, UPDATE_RESTRICTION_UNRESTRICTED)) {
    289             Log.d(TAG, "Unknown restriction: " + mRestriction);
    290             return false;
    291         }
    292 
    293         if (TextUtils.isEmpty(mServerUri)) {
    294             Log.d(TAG, "Missing update server URI");
    295             return false;
    296         }
    297         if (mServerUri.getBytes(StandardCharsets.UTF_8).length > MAX_URI_BYTES) {
    298             Log.d(TAG, "URI bytes exceeded the max: "
    299                     + mServerUri.getBytes(StandardCharsets.UTF_8).length);
    300             return false;
    301         }
    302 
    303         if (TextUtils.isEmpty(mUsername)) {
    304             Log.d(TAG, "Missing username");
    305             return false;
    306         }
    307         if (mUsername.getBytes(StandardCharsets.UTF_8).length > MAX_USERNAME_BYTES) {
    308             Log.d(TAG, "Username bytes exceeded the max: "
    309                     + mUsername.getBytes(StandardCharsets.UTF_8).length);
    310             return false;
    311         }
    312 
    313         if (TextUtils.isEmpty(mBase64EncodedPassword)) {
    314             Log.d(TAG, "Missing username");
    315             return false;
    316         }
    317         if (mBase64EncodedPassword.getBytes(StandardCharsets.UTF_8).length > MAX_PASSWORD_BYTES) {
    318             Log.d(TAG, "Password bytes exceeded the max: "
    319                     + mBase64EncodedPassword.getBytes(StandardCharsets.UTF_8).length);
    320             return false;
    321         }
    322         try {
    323             Base64.decode(mBase64EncodedPassword, Base64.DEFAULT);
    324         } catch (IllegalArgumentException e) {
    325             Log.d(TAG, "Invalid encoding for password: " + mBase64EncodedPassword);
    326             return false;
    327         }
    328 
    329         if (TextUtils.isEmpty(mTrustRootCertUrl)) {
    330             Log.d(TAG, "Missing trust root certificate URL");
    331             return false;
    332         }
    333         if (mTrustRootCertUrl.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) {
    334             Log.d(TAG, "Trust root cert URL bytes exceeded the max: "
    335                     + mTrustRootCertUrl.getBytes(StandardCharsets.UTF_8).length);
    336             return false;
    337         }
    338 
    339         if (mTrustRootCertSha256Fingerprint == null) {
    340             Log.d(TAG, "Missing trust root certificate SHA-256 fingerprint");
    341             return false;
    342         }
    343         if (mTrustRootCertSha256Fingerprint.length != CERTIFICATE_SHA256_BYTES) {
    344             Log.d(TAG, "Incorrect size of trust root certificate SHA-256 fingerprint: "
    345                     + mTrustRootCertSha256Fingerprint.length);
    346             return false;
    347         }
    348         return true;
    349     }
    350 
    351     public static final Creator<UpdateParameter> CREATOR =
    352         new Creator<UpdateParameter>() {
    353             @Override
    354             public UpdateParameter createFromParcel(Parcel in) {
    355                 UpdateParameter updateParam = new UpdateParameter();
    356                 updateParam.setUpdateIntervalInMinutes(in.readLong());
    357                 updateParam.setUpdateMethod(in.readString());
    358                 updateParam.setRestriction(in.readString());
    359                 updateParam.setServerUri(in.readString());
    360                 updateParam.setUsername(in.readString());
    361                 updateParam.setBase64EncodedPassword(in.readString());
    362                 updateParam.setTrustRootCertUrl(in.readString());
    363                 updateParam.setTrustRootCertSha256Fingerprint(in.createByteArray());
    364                 return updateParam;
    365             }
    366 
    367             @Override
    368             public UpdateParameter[] newArray(int size) {
    369                 return new UpdateParameter[size];
    370             }
    371         };
    372 }
    373