blob: 71d4173a389c1c284826e24698e90c2d7f62b76b [file] [log] [blame]
/**
* Copyright (c) 2016, 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;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSp;
import android.net.wifi.hotspot2.pps.Policy;
import android.net.wifi.hotspot2.pps.UpdateParameter;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
import android.os.Parcel;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Class representing Passpoint configuration. This contains configurations specified in
* PerProviderSubscription (PPS) Management Object (MO) tree.
*
* 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 PasspointConfiguration implements Parcelable {
private static final String TAG = "PasspointConfiguration";
/**
* Number of bytes for certificate SHA-256 fingerprint byte array.
*/
private static final int CERTIFICATE_SHA256_BYTES = 32;
/**
* Maximum bytes for URL string.
*/
private static final int MAX_URL_BYTES = 1023;
/**
* Integer value used for indicating null value in the Parcel.
*/
private static final int NULL_VALUE = -1;
/**
* Configurations under HomeSp subtree.
*/
private HomeSp mHomeSp = null;
/**
* Set the Home SP (Service Provider) information.
*
* @param homeSp The Home SP information to set to
*/
public void setHomeSp(HomeSp homeSp) { mHomeSp = homeSp; }
/**
* Get the Home SP (Service Provider) information.
*
* @return Home SP information
*/
public HomeSp getHomeSp() { return mHomeSp; }
/**
* Configurations under Credential subtree.
*/
private Credential mCredential = null;
/**
* Set the credential information.
*
* @param credential The credential information to set to
*/
public void setCredential(Credential credential) {
mCredential = credential;
}
/**
* Get the credential information.
*
* @return credential information
*/
public Credential getCredential() {
return mCredential;
}
/**
* Configurations under Policy subtree.
*/
private Policy mPolicy = null;
/**
* @hide
*/
public void setPolicy(Policy policy) {
mPolicy = policy;
}
/**
* @hide
*/
public Policy getPolicy() {
return mPolicy;
}
/**
* Meta data for performing subscription update.
*/
private UpdateParameter mSubscriptionUpdate = null;
/**
* @hide
*/
public void setSubscriptionUpdate(UpdateParameter subscriptionUpdate) {
mSubscriptionUpdate = subscriptionUpdate;
}
/**
* @hide
*/
public UpdateParameter getSubscriptionUpdate() {
return mSubscriptionUpdate;
}
/**
* List of HTTPS URL for retrieving trust root certificate and the corresponding SHA-256
* fingerprint of the certificate. The certificates are used for verifying AAA server's
* identity during EAP authentication.
*/
private Map<String, byte[]> mTrustRootCertList = null;
/**
* @hide
*/
public void setTrustRootCertList(Map<String, byte[]> trustRootCertList) {
mTrustRootCertList = trustRootCertList;
}
/**
* @hide
*/
public Map<String, byte[]> getTrustRootCertList() {
return mTrustRootCertList;
}
/**
* Set by the subscription server, updated every time the configuration is updated by
* the subscription server.
*
* Use Integer.MIN_VALUE to indicate unset value.
*/
private int mUpdateIdentifier = Integer.MIN_VALUE;
/**
* @hide
*/
public void setUpdateIdentifier(int updateIdentifier) {
mUpdateIdentifier = updateIdentifier;
}
/**
* @hide
*/
public int getUpdateIdentifier() {
return mUpdateIdentifier;
}
/**
* The priority of the credential.
*
* Use Integer.MIN_VALUE to indicate unset value.
*/
private int mCredentialPriority = Integer.MIN_VALUE;
/**
* @hide
*/
public void setCredentialPriority(int credentialPriority) {
mCredentialPriority = credentialPriority;
}
/**
* @hide
*/
public int getCredentialPriority() {
return mCredentialPriority;
}
/**
* The time this subscription is created. It is in the format of number
* of milliseconds since January 1, 1970, 00:00:00 GMT.
*
* Use Long.MIN_VALUE to indicate unset value.
*/
private long mSubscriptionCreationTimeInMillis = Long.MIN_VALUE;
/**
* @hide
*/
public void setSubscriptionCreationTimeInMillis(long subscriptionCreationTimeInMillis) {
mSubscriptionCreationTimeInMillis = subscriptionCreationTimeInMillis;
}
/**
* @hide
*/
public long getSubscriptionCreationTimeInMillis() {
return mSubscriptionCreationTimeInMillis;
}
/**
* The time this subscription will expire. It is in the format of number
* of milliseconds since January 1, 1970, 00:00:00 GMT.
*
* Use Long.MIN_VALUE to indicate unset value.
*/
private long mSubscriptionExpirationTimeInMillis = Long.MIN_VALUE;
/**
* @hide
*/
public void setSubscriptionExpirationTimeInMillis(long subscriptionExpirationTimeInMillis) {
mSubscriptionExpirationTimeInMillis = subscriptionExpirationTimeInMillis;
}
/**
* @hide
*/
public long getSubscriptionExpirationTimeInMillis() {
return mSubscriptionExpirationTimeInMillis;
}
/**
* The type of the subscription. This is defined by the provider and the value is provider
* specific.
*/
private String mSubscriptionType = null;
/**
* @hide
*/
public void setSubscriptionType(String subscriptionType) {
mSubscriptionType = subscriptionType;
}
/**
* @hide
*/
public String getSubscriptionType() {
return mSubscriptionType;
}
/**
* The time period for usage statistics accumulation. A value of zero means that usage
* statistics are not accumulated on a periodic basis (e.g., a one-time limit for
* “pay as you go” - PAYG service). A non-zero value specifies the usage interval in minutes.
*/
private long mUsageLimitUsageTimePeriodInMinutes = Long.MIN_VALUE;
/**
* @hide
*/
public void setUsageLimitUsageTimePeriodInMinutes(long usageLimitUsageTimePeriodInMinutes) {
mUsageLimitUsageTimePeriodInMinutes = usageLimitUsageTimePeriodInMinutes;
}
/**
* @hide
*/
public long getUsageLimitUsageTimePeriodInMinutes() {
return mUsageLimitUsageTimePeriodInMinutes;
}
/**
* The time at which usage statistic accumulation begins. It is in the format of number
* of milliseconds since January 1, 1970, 00:00:00 GMT.
*
* Use Long.MIN_VALUE to indicate unset value.
*/
private long mUsageLimitStartTimeInMillis = Long.MIN_VALUE;
/**
* @hide
*/
public void setUsageLimitStartTimeInMillis(long usageLimitStartTimeInMillis) {
mUsageLimitStartTimeInMillis = usageLimitStartTimeInMillis;
}
/**
* @hide
*/
public long getUsageLimitStartTimeInMillis() {
return mUsageLimitStartTimeInMillis;
}
/**
* The cumulative data limit in megabytes for the {@link #usageLimitUsageTimePeriodInMinutes}.
* A value of zero indicate unlimited data usage.
*
* Use Long.MIN_VALUE to indicate unset value.
*/
private long mUsageLimitDataLimit = Long.MIN_VALUE;
/**
* @hide
*/
public void setUsageLimitDataLimit(long usageLimitDataLimit) {
mUsageLimitDataLimit = usageLimitDataLimit;
}
/**
* @hide
*/
public long getUsageLimitDataLimit() {
return mUsageLimitDataLimit;
}
/**
* The cumulative time limit in minutes for the {@link #usageLimitUsageTimePeriodInMinutes}.
* A value of zero indicate unlimited time usage.
*/
private long mUsageLimitTimeLimitInMinutes = Long.MIN_VALUE;
/**
* @hide
*/
public void setUsageLimitTimeLimitInMinutes(long usageLimitTimeLimitInMinutes) {
mUsageLimitTimeLimitInMinutes = usageLimitTimeLimitInMinutes;
}
/**
* @hide
*/
public long getUsageLimitTimeLimitInMinutes() {
return mUsageLimitTimeLimitInMinutes;
}
/**
* Constructor for creating PasspointConfiguration with default values.
*/
public PasspointConfiguration() {}
/**
* Copy constructor.
*
* @param source The source to copy from
*/
public PasspointConfiguration(PasspointConfiguration source) {
if (source == null) {
return;
}
if (source.mHomeSp != null) {
mHomeSp = new HomeSp(source.mHomeSp);
}
if (source.mCredential != null) {
mCredential = new Credential(source.mCredential);
}
if (source.mPolicy != null) {
mPolicy = new Policy(source.mPolicy);
}
if (source.mTrustRootCertList != null) {
mTrustRootCertList = Collections.unmodifiableMap(source.mTrustRootCertList);
}
if (source.mSubscriptionUpdate != null) {
mSubscriptionUpdate = new UpdateParameter(source.mSubscriptionUpdate);
}
mUpdateIdentifier = source.mUpdateIdentifier;
mCredentialPriority = source.mCredentialPriority;
mSubscriptionCreationTimeInMillis = source.mSubscriptionCreationTimeInMillis;
mSubscriptionExpirationTimeInMillis = source.mSubscriptionExpirationTimeInMillis;
mSubscriptionType = source.mSubscriptionType;
mUsageLimitDataLimit = source.mUsageLimitDataLimit;
mUsageLimitStartTimeInMillis = source.mUsageLimitStartTimeInMillis;
mUsageLimitTimeLimitInMinutes = source.mUsageLimitTimeLimitInMinutes;
mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mHomeSp, flags);
dest.writeParcelable(mCredential, flags);
dest.writeParcelable(mPolicy, flags);
dest.writeParcelable(mSubscriptionUpdate, flags);
writeTrustRootCerts(dest, mTrustRootCertList);
dest.writeInt(mUpdateIdentifier);
dest.writeInt(mCredentialPriority);
dest.writeLong(mSubscriptionCreationTimeInMillis);
dest.writeLong(mSubscriptionExpirationTimeInMillis);
dest.writeString(mSubscriptionType);
dest.writeLong(mUsageLimitUsageTimePeriodInMinutes);
dest.writeLong(mUsageLimitStartTimeInMillis);
dest.writeLong(mUsageLimitDataLimit);
dest.writeLong(mUsageLimitTimeLimitInMinutes);
}
@Override
public boolean equals(Object thatObject) {
if (this == thatObject) {
return true;
}
if (!(thatObject instanceof PasspointConfiguration)) {
return false;
}
PasspointConfiguration that = (PasspointConfiguration) thatObject;
return (mHomeSp == null ? that.mHomeSp == null : mHomeSp.equals(that.mHomeSp))
&& (mCredential == null ? that.mCredential == null
: mCredential.equals(that.mCredential))
&& (mPolicy == null ? that.mPolicy == null : mPolicy.equals(that.mPolicy))
&& (mSubscriptionUpdate == null ? that.mSubscriptionUpdate == null
: mSubscriptionUpdate.equals(that.mSubscriptionUpdate))
&& isTrustRootCertListEquals(mTrustRootCertList, that.mTrustRootCertList)
&& mUpdateIdentifier == that.mUpdateIdentifier
&& mCredentialPriority == that.mCredentialPriority
&& mSubscriptionCreationTimeInMillis == that.mSubscriptionCreationTimeInMillis
&& mSubscriptionExpirationTimeInMillis == that.mSubscriptionExpirationTimeInMillis
&& TextUtils.equals(mSubscriptionType, that.mSubscriptionType)
&& mUsageLimitUsageTimePeriodInMinutes == that.mUsageLimitUsageTimePeriodInMinutes
&& mUsageLimitStartTimeInMillis == that.mUsageLimitStartTimeInMillis
&& mUsageLimitDataLimit == that.mUsageLimitDataLimit
&& mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes;
}
@Override
public int hashCode() {
return Objects.hash(mHomeSp, mCredential, mPolicy, mSubscriptionUpdate, mTrustRootCertList,
mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis,
mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes,
mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("UpdateIdentifier: ").append(mUpdateIdentifier).append("\n");
builder.append("CredentialPriority: ").append(mCredentialPriority).append("\n");
builder.append("SubscriptionCreationTime: ").append(
mSubscriptionCreationTimeInMillis != Long.MIN_VALUE
? new Date(mSubscriptionCreationTimeInMillis) : "Not specified").append("\n");
builder.append("SubscriptionExpirationTime: ").append(
mSubscriptionExpirationTimeInMillis != Long.MIN_VALUE
? new Date(mSubscriptionExpirationTimeInMillis) : "Not specified").append("\n");
builder.append("UsageLimitStartTime: ").append(mUsageLimitStartTimeInMillis != Long.MIN_VALUE
? new Date(mUsageLimitStartTimeInMillis) : "Not specified").append("\n");
builder.append("UsageTimePeriod: ").append(mUsageLimitUsageTimePeriodInMinutes)
.append("\n");
builder.append("UsageLimitDataLimit: ").append(mUsageLimitDataLimit).append("\n");
builder.append("UsageLimitTimeLimit: ").append(mUsageLimitTimeLimitInMinutes).append("\n");
if (mHomeSp != null) {
builder.append("HomeSP Begin ---\n");
builder.append(mHomeSp);
builder.append("HomeSP End ---\n");
}
if (mCredential != null) {
builder.append("Credential Begin ---\n");
builder.append(mCredential);
builder.append("Credential End ---\n");
}
if (mPolicy != null) {
builder.append("Policy Begin ---\n");
builder.append(mPolicy);
builder.append("Policy End ---\n");
}
if (mSubscriptionUpdate != null) {
builder.append("SubscriptionUpdate Begin ---\n");
builder.append(mSubscriptionUpdate);
builder.append("SubscriptionUpdate End ---\n");
}
if (mTrustRootCertList != null) {
builder.append("TrustRootCertServers: ").append(mTrustRootCertList.keySet())
.append("\n");
}
return builder.toString();
}
/**
* Validate the configuration data.
*
* @return true on success or false on failure
* @hide
*/
public boolean validate() {
if (mHomeSp == null || !mHomeSp.validate()) {
return false;
}
if (mCredential == null || !mCredential.validate()) {
return false;
}
if (mPolicy != null && !mPolicy.validate()) {
return false;
}
if (mSubscriptionUpdate != null && !mSubscriptionUpdate.validate()) {
return false;
}
if (mTrustRootCertList != null) {
for (Map.Entry<String, byte[]> entry : mTrustRootCertList.entrySet()) {
String url = entry.getKey();
byte[] certFingerprint = entry.getValue();
if (TextUtils.isEmpty(url)) {
Log.d(TAG, "Empty URL");
return false;
}
if (url.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) {
Log.d(TAG, "URL bytes exceeded the max: "
+ url.getBytes(StandardCharsets.UTF_8).length);
return false;
}
if (certFingerprint == null) {
Log.d(TAG, "Fingerprint not specified");
return false;
}
if (certFingerprint.length != CERTIFICATE_SHA256_BYTES) {
Log.d(TAG, "Incorrect size of trust root certificate SHA-256 fingerprint: "
+ certFingerprint.length);
return false;
}
}
}
return true;
}
public static final Creator<PasspointConfiguration> CREATOR =
new Creator<PasspointConfiguration>() {
@Override
public PasspointConfiguration createFromParcel(Parcel in) {
PasspointConfiguration config = new PasspointConfiguration();
config.setHomeSp(in.readParcelable(null));
config.setCredential(in.readParcelable(null));
config.setPolicy(in.readParcelable(null));
config.setSubscriptionUpdate(in.readParcelable(null));
config.setTrustRootCertList(readTrustRootCerts(in));
config.setUpdateIdentifier(in.readInt());
config.setCredentialPriority(in.readInt());
config.setSubscriptionCreationTimeInMillis(in.readLong());
config.setSubscriptionExpirationTimeInMillis(in.readLong());
config.setSubscriptionType(in.readString());
config.setUsageLimitUsageTimePeriodInMinutes(in.readLong());
config.setUsageLimitStartTimeInMillis(in.readLong());
config.setUsageLimitDataLimit(in.readLong());
config.setUsageLimitTimeLimitInMinutes(in.readLong());
return config;
}
@Override
public PasspointConfiguration[] newArray(int size) {
return new PasspointConfiguration[size];
}
/**
* Helper function for reading trust root certificate info list from a Parcel.
*
* @param in The Parcel to read from
* @return The list of trust root certificate URL with the corresponding certificate
* fingerprint
*/
private Map<String, byte[]> readTrustRootCerts(Parcel in) {
int size = in.readInt();
if (size == NULL_VALUE) {
return null;
}
Map<String, byte[]> trustRootCerts = new HashMap<>(size);
for (int i = 0; i < size; i++) {
String key = in.readString();
byte[] value = in.createByteArray();
trustRootCerts.put(key, value);
}
return trustRootCerts;
}
};
/**
* Helper function for writing trust root certificate information list.
*
* @param dest The Parcel to write to
* @param trustRootCerts The list of trust root certificate URL with the corresponding
* certificate fingerprint
*/
private static void writeTrustRootCerts(Parcel dest, Map<String, byte[]> trustRootCerts) {
if (trustRootCerts == null) {
dest.writeInt(NULL_VALUE);
return;
}
dest.writeInt(trustRootCerts.size());
for (Map.Entry<String, byte[]> entry : trustRootCerts.entrySet()) {
dest.writeString(entry.getKey());
dest.writeByteArray(entry.getValue());
}
}
/**
* Helper function for comparing two trust root certificate list. Cannot use Map#equals
* method since the value type (byte[]) doesn't override equals method.
*
* @param list1 The first trust root certificate list
* @param list2 The second trust root certificate list
* @return true if the two list are equal
*/
private static boolean isTrustRootCertListEquals(Map<String, byte[]> list1,
Map<String, byte[]> list2) {
if (list1 == null || list2 == null) {
return list1 == list2;
}
if (list1.size() != list2.size()) {
return false;
}
for (Map.Entry<String, byte[]> entry : list1.entrySet()) {
if (!Arrays.equals(entry.getValue(), list2.get(entry.getKey()))) {
return false;
}
}
return true;
}
}