/** * Copyright (c) 2017, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.wifi.hotspot2.pps; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.Base64; import android.util.Log; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Objects; /** * Class representing configuration parameters for subscription or policy update in * PerProviderSubscription (PPS) Management Object (MO) tree. This is used by both * PerProviderSubscription/Policy/PolicyUpdate and PerProviderSubscription/SubscriptionUpdate * subtree. * * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0 * Release 2 Technical Specification. */ public final class UpdateParameter implements Parcelable { private static final String TAG = "UpdateParameter"; /** * Value indicating policy update is not applicable. Thus, never check with policy server * for updates. */ public static final long UPDATE_CHECK_INTERVAL_NEVER = 0xFFFFFFFFL; /** * Valid string for UpdateMethod. */ public static final String UPDATE_METHOD_OMADM = "OMA-DM-ClientInitiated"; public static final String UPDATE_METHOD_SSP = "SSP-ClientInitiated"; /** * Valid string for Restriction. */ public static final String UPDATE_RESTRICTION_HOMESP = "HomeSP"; public static final String UPDATE_RESTRICTION_ROAMING_PARTNER = "RoamingPartner"; public static final String UPDATE_RESTRICTION_UNRESTRICTED = "Unrestricted"; /** * Maximum bytes for URI string. */ private static final int MAX_URI_BYTES = 1023; /** * Maximum bytes for URI string. */ private static final int MAX_URL_BYTES = 1023; /** * Maximum bytes for username. */ private static final int MAX_USERNAME_BYTES = 63; /** * Maximum bytes for password. */ private static final int MAX_PASSWORD_BYTES = 255; /** * Number of bytes for certificate SHA-256 fingerprint byte array. */ private static final int CERTIFICATE_SHA256_BYTES = 32; /** * This specifies how often the mobile device shall check with policy server for updates. * * Using Long.MIN_VALUE to indicate unset value. */ private long mUpdateIntervalInMinutes = Long.MIN_VALUE; public void setUpdateIntervalInMinutes(long updateIntervalInMinutes) { mUpdateIntervalInMinutes = updateIntervalInMinutes; } public long getUpdateIntervalInMinutes() { return mUpdateIntervalInMinutes; } /** * The method used to update the policy. Permitted values are "OMA-DM-ClientInitiated" * and "SPP-ClientInitiated". */ private String mUpdateMethod = null; public void setUpdateMethod(String updateMethod) { mUpdateMethod = updateMethod; } public String getUpdateMethod() { return mUpdateMethod; } /** * This specifies the hotspots at which the subscription update is permitted. Permitted * values are "HomeSP", "RoamingPartner", or "Unrestricted"; */ private String mRestriction = null; public void setRestriction(String restriction) { mRestriction = restriction; } public String getRestriction() { return mRestriction; } /** * The URI of the update server. */ private String mServerUri = null; public void setServerUri(String serverUri) { mServerUri = serverUri; } public String getServerUri() { return mServerUri; } /** * Username used to authenticate with the policy server. */ private String mUsername = null; public void setUsername(String username) { mUsername = username; } public String getUsername() { return mUsername; } /** * Base64 encoded password used to authenticate with the policy server. */ private String mBase64EncodedPassword = null; public void setBase64EncodedPassword(String password) { mBase64EncodedPassword = password; } public String getBase64EncodedPassword() { return mBase64EncodedPassword; } /** * HTTPS URL for retrieving certificate for trust root. The trust root is used to validate * policy server's identity. */ private String mTrustRootCertUrl = null; public void setTrustRootCertUrl(String trustRootCertUrl) { mTrustRootCertUrl = trustRootCertUrl; } public String getTrustRootCertUrl() { return mTrustRootCertUrl; } /** * SHA-256 fingerprint of the certificate located at {@link #trustRootCertUrl} */ private byte[] mTrustRootCertSha256Fingerprint = null; public void setTrustRootCertSha256Fingerprint(byte[] fingerprint) { mTrustRootCertSha256Fingerprint = fingerprint; } public byte[] getTrustRootCertSha256Fingerprint() { return mTrustRootCertSha256Fingerprint; } /** * Constructor for creating Policy with default values. */ public UpdateParameter() {} /** * Copy constructor. * * @param source The source to copy from */ public UpdateParameter(UpdateParameter source) { if (source == null) { return; } mUpdateIntervalInMinutes = source.mUpdateIntervalInMinutes; mUpdateMethod = source.mUpdateMethod; mRestriction = source.mRestriction; mServerUri = source.mServerUri; mUsername = source.mUsername; mBase64EncodedPassword = source.mBase64EncodedPassword; mTrustRootCertUrl = source.mTrustRootCertUrl; if (source.mTrustRootCertSha256Fingerprint != null) { mTrustRootCertSha256Fingerprint = Arrays.copyOf(source.mTrustRootCertSha256Fingerprint, source.mTrustRootCertSha256Fingerprint.length); } } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(mUpdateIntervalInMinutes); dest.writeString(mUpdateMethod); dest.writeString(mRestriction); dest.writeString(mServerUri); dest.writeString(mUsername); dest.writeString(mBase64EncodedPassword); dest.writeString(mTrustRootCertUrl); dest.writeByteArray(mTrustRootCertSha256Fingerprint); } @Override public boolean equals(Object thatObject) { if (this == thatObject) { return true; } if (!(thatObject instanceof UpdateParameter)) { return false; } UpdateParameter that = (UpdateParameter) thatObject; return mUpdateIntervalInMinutes == that.mUpdateIntervalInMinutes && TextUtils.equals(mUpdateMethod, that.mUpdateMethod) && TextUtils.equals(mRestriction, that.mRestriction) && TextUtils.equals(mServerUri, that.mServerUri) && TextUtils.equals(mUsername, that.mUsername) && TextUtils.equals(mBase64EncodedPassword, that.mBase64EncodedPassword) && TextUtils.equals(mTrustRootCertUrl, that.mTrustRootCertUrl) && Arrays.equals(mTrustRootCertSha256Fingerprint, that.mTrustRootCertSha256Fingerprint); } @Override public int hashCode() { return Objects.hash(mUpdateIntervalInMinutes, mUpdateMethod, mRestriction, mServerUri, mUsername, mBase64EncodedPassword, mTrustRootCertUrl, mTrustRootCertSha256Fingerprint); } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("UpdateInterval: ").append(mUpdateIntervalInMinutes).append("\n"); builder.append("UpdateMethod: ").append(mUpdateMethod).append("\n"); builder.append("Restriction: ").append(mRestriction).append("\n"); builder.append("ServerURI: ").append(mServerUri).append("\n"); builder.append("Username: ").append(mUsername).append("\n"); builder.append("TrustRootCertURL: ").append(mTrustRootCertUrl).append("\n"); return builder.toString(); } /** * Validate UpdateParameter data. * * @return true on success * @hide */ public boolean validate() { if (mUpdateIntervalInMinutes == Long.MIN_VALUE) { Log.d(TAG, "Update interval not specified"); return false; } // Update not applicable. if (mUpdateIntervalInMinutes == UPDATE_CHECK_INTERVAL_NEVER) { return true; } if (!TextUtils.equals(mUpdateMethod, UPDATE_METHOD_OMADM) && !TextUtils.equals(mUpdateMethod, UPDATE_METHOD_SSP)) { Log.d(TAG, "Unknown update method: " + mUpdateMethod); return false; } if (!TextUtils.equals(mRestriction, UPDATE_RESTRICTION_HOMESP) && !TextUtils.equals(mRestriction, UPDATE_RESTRICTION_ROAMING_PARTNER) && !TextUtils.equals(mRestriction, UPDATE_RESTRICTION_UNRESTRICTED)) { Log.d(TAG, "Unknown restriction: " + mRestriction); return false; } if (TextUtils.isEmpty(mServerUri)) { Log.d(TAG, "Missing update server URI"); return false; } if (mServerUri.getBytes(StandardCharsets.UTF_8).length > MAX_URI_BYTES) { Log.d(TAG, "URI bytes exceeded the max: " + mServerUri.getBytes(StandardCharsets.UTF_8).length); return false; } if (TextUtils.isEmpty(mUsername)) { Log.d(TAG, "Missing username"); return false; } if (mUsername.getBytes(StandardCharsets.UTF_8).length > MAX_USERNAME_BYTES) { Log.d(TAG, "Username bytes exceeded the max: " + mUsername.getBytes(StandardCharsets.UTF_8).length); return false; } if (TextUtils.isEmpty(mBase64EncodedPassword)) { Log.d(TAG, "Missing username"); return false; } if (mBase64EncodedPassword.getBytes(StandardCharsets.UTF_8).length > MAX_PASSWORD_BYTES) { Log.d(TAG, "Password bytes exceeded the max: " + mBase64EncodedPassword.getBytes(StandardCharsets.UTF_8).length); return false; } try { Base64.decode(mBase64EncodedPassword, Base64.DEFAULT); } catch (IllegalArgumentException e) { Log.d(TAG, "Invalid encoding for password: " + mBase64EncodedPassword); return false; } if (TextUtils.isEmpty(mTrustRootCertUrl)) { Log.d(TAG, "Missing trust root certificate URL"); return false; } if (mTrustRootCertUrl.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) { Log.d(TAG, "Trust root cert URL bytes exceeded the max: " + mTrustRootCertUrl.getBytes(StandardCharsets.UTF_8).length); return false; } if (mTrustRootCertSha256Fingerprint == null) { Log.d(TAG, "Missing trust root certificate SHA-256 fingerprint"); return false; } if (mTrustRootCertSha256Fingerprint.length != CERTIFICATE_SHA256_BYTES) { Log.d(TAG, "Incorrect size of trust root certificate SHA-256 fingerprint: " + mTrustRootCertSha256Fingerprint.length); return false; } return true; } public static final Creator<UpdateParameter> CREATOR = new Creator<UpdateParameter>() { @Override public UpdateParameter createFromParcel(Parcel in) { UpdateParameter updateParam = new UpdateParameter(); updateParam.setUpdateIntervalInMinutes(in.readLong()); updateParam.setUpdateMethod(in.readString()); updateParam.setRestriction(in.readString()); updateParam.setServerUri(in.readString()); updateParam.setUsername(in.readString()); updateParam.setBase64EncodedPassword(in.readString()); updateParam.setTrustRootCertUrl(in.readString()); updateParam.setTrustRootCertSha256Fingerprint(in.createByteArray()); return updateParam; } @Override public UpdateParameter[] newArray(int size) { return new UpdateParameter[size]; } }; }