hotspot2: added remaining parameters to PasspointConfiguration am: ddf6fa06c6
am: 44bcb75926
Change-Id: Ibb587721014dd25243f7423864f1d5019ee42fde
This commit is contained in:
@@ -19,9 +19,18 @@ 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.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Class representing Passpoint configuration. This contains configurations specified in
|
||||
* PerProviderSubscription (PPS) Management Object (MO) tree.
|
||||
@@ -32,10 +41,106 @@ import android.os.Parcel;
|
||||
* @hide
|
||||
*/
|
||||
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;
|
||||
|
||||
public HomeSP homeSp = null;
|
||||
public Credential credential = null;
|
||||
public Policy policy = null;
|
||||
|
||||
/**
|
||||
* Meta data for performing subscription update.
|
||||
*/
|
||||
public UpdateParameter subscriptionUpdate = null;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public Map<String, byte[]> trustRootCertList = null;
|
||||
|
||||
/**
|
||||
* Set by the subscription server, updated every time the configuration is updated by
|
||||
* the subscription server.
|
||||
*
|
||||
* Use Integer.MIN_VALUE to indicate unset value.
|
||||
*/
|
||||
public int updateIdentifier = Integer.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* The priority of the credential.
|
||||
*
|
||||
* Use Integer.MIN_VALUE to indicate unset value.
|
||||
*/
|
||||
public int credentialPriority = Integer.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public long subscriptionCreationTimeInMs = Long.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public long subscriptionExpirationTimeInMs = Long.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* The type of the subscription. This is defined by the provider and the value is provider
|
||||
* specific.
|
||||
*/
|
||||
public String subscriptionType = null;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public long usageLimitUsageTimePeriodInMinutes = Long.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public long usageLimitStartTimeInMs = Long.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public long usageLimitDataLimit = Long.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* The cumulative time limit in minutes for the {@link #usageLimitUsageTimePeriodInMinutes}.
|
||||
* A value of zero indicate unlimited time usage.
|
||||
*/
|
||||
public long usageLimitTimeLimitInMinutes = Long.MIN_VALUE;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for creating PasspointConfiguration with default values.
|
||||
*/
|
||||
@@ -47,17 +152,34 @@ public final class PasspointConfiguration implements Parcelable {
|
||||
* @param source The source to copy from
|
||||
*/
|
||||
public PasspointConfiguration(PasspointConfiguration source) {
|
||||
if (source != null) {
|
||||
if (source.homeSp != null) {
|
||||
homeSp = new HomeSP(source.homeSp);
|
||||
}
|
||||
if (source.credential != null) {
|
||||
credential = new Credential(source.credential);
|
||||
}
|
||||
if (source.policy != null) {
|
||||
policy = new Policy(source.policy);
|
||||
}
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (source.homeSp != null) {
|
||||
homeSp = new HomeSP(source.homeSp);
|
||||
}
|
||||
if (source.credential != null) {
|
||||
credential = new Credential(source.credential);
|
||||
}
|
||||
if (source.policy != null) {
|
||||
policy = new Policy(source.policy);
|
||||
}
|
||||
if (source.trustRootCertList != null) {
|
||||
trustRootCertList = Collections.unmodifiableMap(source.trustRootCertList);
|
||||
}
|
||||
if (source.subscriptionUpdate != null) {
|
||||
subscriptionUpdate = new UpdateParameter(source.subscriptionUpdate);
|
||||
}
|
||||
updateIdentifier = source.updateIdentifier;
|
||||
credentialPriority = source.credentialPriority;
|
||||
subscriptionCreationTimeInMs = source.subscriptionCreationTimeInMs;
|
||||
subscriptionExpirationTimeInMs = source.subscriptionExpirationTimeInMs;
|
||||
subscriptionType = source.subscriptionType;
|
||||
usageLimitDataLimit = source.usageLimitDataLimit;
|
||||
usageLimitStartTimeInMs = source.usageLimitStartTimeInMs;
|
||||
usageLimitTimeLimitInMinutes = source.usageLimitTimeLimitInMinutes;
|
||||
usageLimitUsageTimePeriodInMinutes = source.usageLimitUsageTimePeriodInMinutes;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,6 +192,17 @@ public final class PasspointConfiguration implements Parcelable {
|
||||
dest.writeParcelable(homeSp, flags);
|
||||
dest.writeParcelable(credential, flags);
|
||||
dest.writeParcelable(policy, flags);
|
||||
dest.writeParcelable(subscriptionUpdate, flags);
|
||||
writeTrustRootCerts(dest, trustRootCertList);
|
||||
dest.writeInt(updateIdentifier);
|
||||
dest.writeInt(credentialPriority);
|
||||
dest.writeLong(subscriptionCreationTimeInMs);
|
||||
dest.writeLong(subscriptionExpirationTimeInMs);
|
||||
dest.writeString(subscriptionType);
|
||||
dest.writeLong(usageLimitUsageTimePeriodInMinutes);
|
||||
dest.writeLong(usageLimitStartTimeInMs);
|
||||
dest.writeLong(usageLimitDataLimit);
|
||||
dest.writeLong(usageLimitTimeLimitInMinutes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -82,9 +215,21 @@ public final class PasspointConfiguration implements Parcelable {
|
||||
}
|
||||
PasspointConfiguration that = (PasspointConfiguration) thatObject;
|
||||
return (homeSp == null ? that.homeSp == null : homeSp.equals(that.homeSp))
|
||||
&& (credential == null ? that.credential == null :
|
||||
credential.equals(that.credential))
|
||||
&& (policy == null) ? that.policy == null : policy.equals(that.policy);
|
||||
&& (credential == null ? that.credential == null
|
||||
: credential.equals(that.credential))
|
||||
&& (policy == null) ? that.policy == null : policy.equals(that.policy)
|
||||
&& (subscriptionUpdate == null) ? that.subscriptionUpdate == null
|
||||
: subscriptionUpdate.equals(that.subscriptionUpdate)
|
||||
&& isTrustRootCertListEquals(trustRootCertList, that.trustRootCertList)
|
||||
&& updateIdentifier == that.updateIdentifier
|
||||
&& credentialPriority == that.credentialPriority
|
||||
&& subscriptionCreationTimeInMs == that.subscriptionCreationTimeInMs
|
||||
&& subscriptionExpirationTimeInMs == that.subscriptionExpirationTimeInMs
|
||||
&& TextUtils.equals(subscriptionType, that.subscriptionType)
|
||||
&& usageLimitUsageTimePeriodInMinutes == that.usageLimitUsageTimePeriodInMinutes
|
||||
&& usageLimitStartTimeInMs == that.usageLimitStartTimeInMs
|
||||
&& usageLimitDataLimit == that.usageLimitDataLimit
|
||||
&& usageLimitTimeLimitInMinutes == that .usageLimitTimeLimitInMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,6 +247,34 @@ public final class PasspointConfiguration implements Parcelable {
|
||||
if (policy != null && !policy.validate()) {
|
||||
return false;
|
||||
}
|
||||
if (subscriptionUpdate != null && !subscriptionUpdate.validate()) {
|
||||
return false;
|
||||
}
|
||||
if (trustRootCertList != null) {
|
||||
for (Map.Entry<String, byte[]> entry : trustRootCertList.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;
|
||||
}
|
||||
|
||||
@@ -113,11 +286,87 @@ public final class PasspointConfiguration implements Parcelable {
|
||||
config.homeSp = in.readParcelable(null);
|
||||
config.credential = in.readParcelable(null);
|
||||
config.policy = in.readParcelable(null);
|
||||
config.subscriptionUpdate = in.readParcelable(null);
|
||||
config.trustRootCertList = readTrustRootCerts(in);
|
||||
config.updateIdentifier = in.readInt();
|
||||
config.credentialPriority = in.readInt();
|
||||
config.subscriptionCreationTimeInMs = in.readLong();
|
||||
config.subscriptionExpirationTimeInMs = in.readLong();
|
||||
config.subscriptionType = in.readString();
|
||||
config.usageLimitUsageTimePeriodInMinutes = in.readLong();
|
||||
config.usageLimitStartTimeInMs = in.readLong();
|
||||
config.usageLimitDataLimit = in.readLong();
|
||||
config.usageLimitTimeLimitInMinutes = 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +132,20 @@ public final class PPSMOParser {
|
||||
*/
|
||||
private static final String NODE_PER_PROVIDER_SUBSCRIPTION = "PerProviderSubscription";
|
||||
|
||||
/**
|
||||
* Fields under PerProviderSubscription.
|
||||
*/
|
||||
private static final String NODE_UPDATE_IDENTIFIER = "UpdateIdentifier";
|
||||
private static final String NODE_AAA_SERVER_TRUST_ROOT = "AAAServerTrustRoot";
|
||||
private static final String NODE_SUBSCRIPTION_UPDATE = "SubscriptionUpdate";
|
||||
private static final String NODE_SUBSCRIPTION_PARAMETER = "SubscriptionParameter";
|
||||
private static final String NODE_TYPE_OF_SUBSCRIPTION = "TypeOfSubscription";
|
||||
private static final String NODE_USAGE_LIMITS = "UsageLimits";
|
||||
private static final String NODE_DATA_LIMIT = "DataLimit";
|
||||
private static final String NODE_START_DATE = "StartDate";
|
||||
private static final String NODE_TIME_LIMIT = "TimeLimit";
|
||||
private static final String NODE_USAGE_TIME_PERIOD = "UsageTimePeriod";
|
||||
private static final String NODE_CREDENTIAL_PRIORITY = "CredentialPriority";
|
||||
/**
|
||||
* Fields under HomeSP subtree.
|
||||
*/
|
||||
@@ -378,6 +392,10 @@ public final class PPSMOParser {
|
||||
* ...
|
||||
* </RTPProperties>
|
||||
* <Node>
|
||||
* <NodeName>UpdateIdentifier</NodeName>
|
||||
* <Value>...</Value>
|
||||
* </Node>
|
||||
* <Node>
|
||||
* ...
|
||||
* </Node>
|
||||
* </Node>
|
||||
@@ -390,11 +408,12 @@ public final class PPSMOParser {
|
||||
throws ParsingException {
|
||||
PasspointConfiguration config = null;
|
||||
String nodeName = null;
|
||||
int updateIdentifier = Integer.MIN_VALUE;
|
||||
for (XMLNode child : node.getChildren()) {
|
||||
switch (child.getTag()) {
|
||||
case TAG_NODE_NAME:
|
||||
if (nodeName != null) {
|
||||
throw new ParsingException("Duplicant NodeName: " + child.getText());
|
||||
throw new ParsingException("Duplicate NodeName: " + child.getText());
|
||||
}
|
||||
nodeName = child.getText();
|
||||
if (!TextUtils.equals(nodeName, NODE_PER_PROVIDER_SUBSCRIPTION)) {
|
||||
@@ -402,13 +421,22 @@ public final class PPSMOParser {
|
||||
}
|
||||
break;
|
||||
case TAG_NODE:
|
||||
// Only one PerProviderSubscription instance is expected and allowed.
|
||||
if (config != null) {
|
||||
throw new ParsingException("Multiple PPS instance");
|
||||
// A node can be either an UpdateIdentifier node or a PerProviderSubscription
|
||||
// instance node. Flatten out the XML tree first by converting it to a PPS
|
||||
// tree to reduce the complexity of the parsing code.
|
||||
PPSNode ppsNodeRoot = buildPpsNode(child);
|
||||
if (TextUtils.equals(ppsNodeRoot.getName(), NODE_UPDATE_IDENTIFIER)) {
|
||||
if (updateIdentifier != Integer.MIN_VALUE) {
|
||||
throw new ParsingException("Multiple node for UpdateIdentifier");
|
||||
}
|
||||
updateIdentifier = parseInteger(getPpsNodeValue(ppsNodeRoot));
|
||||
} else {
|
||||
// Only one PerProviderSubscription instance is expected and allowed.
|
||||
if (config != null) {
|
||||
throw new ParsingException("Multiple PPS instance");
|
||||
}
|
||||
config = parsePpsInstance(ppsNodeRoot);
|
||||
}
|
||||
// Convert the XML tree to a PPS tree.
|
||||
PPSNode ppsInstanceRoot = buildPpsNode(child);
|
||||
config = parsePpsInstance(ppsInstanceRoot);
|
||||
break;
|
||||
case TAG_RT_PROPERTIES:
|
||||
// Parse and verify URN stored in the RT (Run Time) Properties.
|
||||
@@ -421,6 +449,9 @@ public final class PPSMOParser {
|
||||
throw new ParsingException("Unknown tag under PPS node: " + child.getTag());
|
||||
}
|
||||
}
|
||||
if (config != null && updateIdentifier != Integer.MIN_VALUE) {
|
||||
config.updateIdentifier = updateIdentifier;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -583,6 +614,18 @@ public final class PPSMOParser {
|
||||
case NODE_POLICY:
|
||||
config.policy = parsePolicy(child);
|
||||
break;
|
||||
case NODE_AAA_SERVER_TRUST_ROOT:
|
||||
config.trustRootCertList = parseAAAServerTrustRootList(child);
|
||||
break;
|
||||
case NODE_SUBSCRIPTION_UPDATE:
|
||||
config.subscriptionUpdate = parseUpdateParameter(child);
|
||||
break;
|
||||
case NODE_SUBSCRIPTION_PARAMETER:
|
||||
parseSubscriptionParameter(child, config);
|
||||
break;
|
||||
case NODE_CREDENTIAL_PRIORITY:
|
||||
config.credentialPriority = parseInteger(getPpsNodeValue(child));
|
||||
break;
|
||||
default:
|
||||
throw new ParsingException("Unknown node: " + child.getName());
|
||||
}
|
||||
@@ -648,11 +691,7 @@ public final class PPSMOParser {
|
||||
String[] oiStrArray = oiStr.split(",");
|
||||
long[] oiArray = new long[oiStrArray.length];
|
||||
for (int i = 0; i < oiStrArray.length; i++) {
|
||||
try {
|
||||
oiArray[i] = Long.parseLong(oiStrArray[i], 16);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException("Invalid OI: " + oiStrArray[i]);
|
||||
}
|
||||
oiArray[i] = parseLong(oiStrArray[i], 16);
|
||||
}
|
||||
return oiArray;
|
||||
}
|
||||
@@ -703,11 +742,7 @@ public final class PPSMOParser {
|
||||
ssid = getPpsNodeValue(child);
|
||||
break;
|
||||
case NODE_HESSID:
|
||||
try {
|
||||
hessid = Long.parseLong(getPpsNodeValue(child), 16);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException("Invalid HESSID: " + getPpsNodeValue(child));
|
||||
}
|
||||
hessid = parseLong(getPpsNodeValue(child), 16);
|
||||
break;
|
||||
default:
|
||||
throw new ParsingException("Unknown node under NetworkID instance: " +
|
||||
@@ -1184,20 +1219,10 @@ public final class PPSMOParser {
|
||||
networkType = getPpsNodeValue(child);
|
||||
break;
|
||||
case NODE_DOWNLINK_BANDWIDTH:
|
||||
try {
|
||||
downlinkBandwidth = Long.parseLong(getPpsNodeValue(child));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException("Invalid value for downlink bandwidth: "
|
||||
+ getPpsNodeValue(child));
|
||||
}
|
||||
downlinkBandwidth = parseLong(getPpsNodeValue(child), 10);
|
||||
break;
|
||||
case NODE_UPLINK_BANDWIDTH:
|
||||
try {
|
||||
uplinkBandwidth = Long.parseLong(getPpsNodeValue(child));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException("Invalid value for downlink bandwidth: "
|
||||
+ getPpsNodeValue(child));
|
||||
}
|
||||
uplinkBandwidth = parseLong(getPpsNodeValue(child), 10);
|
||||
break;
|
||||
default:
|
||||
throw new ParsingException("Unknown node under MinBackhaulThreshold instance "
|
||||
@@ -1239,13 +1264,7 @@ public final class PPSMOParser {
|
||||
for (PPSNode child : node.getChildren()) {
|
||||
switch(child.getName()) {
|
||||
case NODE_UPDATE_INTERVAL:
|
||||
try {
|
||||
updateParam.updateIntervalInMinutes =
|
||||
Long.parseLong(getPpsNodeValue(child));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException("Invalid value for update interval: "
|
||||
+ getPpsNodeValue(child));
|
||||
}
|
||||
updateParam.updateIntervalInMinutes = parseLong(getPpsNodeValue(child), 10);
|
||||
break;
|
||||
case NODE_UPDATE_METHOD:
|
||||
updateParam.updateMethod = getPpsNodeValue(child);
|
||||
@@ -1262,7 +1281,7 @@ public final class PPSMOParser {
|
||||
updateParam.base64EncodedPassword = usernamePassword.second;
|
||||
break;
|
||||
case NODE_TRUST_ROOT:
|
||||
Pair<String, byte[]> trustRoot = parseUpdateTrustRoot(child);
|
||||
Pair<String, byte[]> trustRoot = parseTrustRoot(child);
|
||||
updateParam.trustRootCertUrl = trustRoot.first;
|
||||
updateParam.trustRootCertSha256Fingerprint = trustRoot.second;
|
||||
break;
|
||||
@@ -1312,16 +1331,19 @@ public final class PPSMOParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the trust root parameters associated with policy or subscription update.
|
||||
* Parse the trust root parameters associated with policy update, subscription update, or AAA
|
||||
* server trust root.
|
||||
*
|
||||
* This contained configurations under either
|
||||
* PerProviderSubscription/Policy/PolicyUpdate/TrustRoot or
|
||||
* PerProviderSubscription/SubscriptionUpdate/TrustRoot subtree.
|
||||
* PerProviderSubscription/SubscriptionUpdate/TrustRoot or
|
||||
* PerProviderSubscription/AAAServerTrustRoot/<X+> subtree.
|
||||
*
|
||||
* @param node PPSNode representing the root of the TrustRoot subtree
|
||||
* @return Pair of Certificate URL and fingerprint
|
||||
* @throws ParsingException
|
||||
*/
|
||||
private static Pair<String, byte[]> parseUpdateTrustRoot(PPSNode node)
|
||||
private static Pair<String, byte[]> parseTrustRoot(PPSNode node)
|
||||
throws ParsingException {
|
||||
if (node.isLeaf()) {
|
||||
throw new ParsingException("Leaf node not expected for TrustRoot");
|
||||
@@ -1449,6 +1471,97 @@ public final class PPSMOParser {
|
||||
return Pair.create(proto, ports);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse configurations under PerProviderSubscription/AAAServerTrustRoot subtree.
|
||||
*
|
||||
* @param node PPSNode representing the root of PerProviderSubscription/AAAServerTrustRoot
|
||||
* subtree
|
||||
* @return Map of certificate URL with the corresponding certificate fingerprint
|
||||
* @throws ParsingException
|
||||
*/
|
||||
private static Map<String, byte[]> parseAAAServerTrustRootList(PPSNode node)
|
||||
throws ParsingException {
|
||||
if (node.isLeaf()) {
|
||||
throw new ParsingException("Leaf node not expected for AAAServerTrustRoot");
|
||||
}
|
||||
Map<String, byte[]> certList = new HashMap<>();
|
||||
for (PPSNode child : node.getChildren()) {
|
||||
Pair<String, byte[]> certTuple = parseTrustRoot(child);
|
||||
certList.put(certTuple.first, certTuple.second);
|
||||
}
|
||||
return certList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse configurations under PerProviderSubscription/SubscriptionParameter subtree.
|
||||
*
|
||||
* @param node PPSNode representing the root of PerProviderSubscription/SubscriptionParameter
|
||||
* subtree
|
||||
* @param config Instance of {@link PasspointConfiguration}
|
||||
* @throws ParsingException
|
||||
*/
|
||||
private static void parseSubscriptionParameter(PPSNode node, PasspointConfiguration config)
|
||||
throws ParsingException {
|
||||
if (node.isLeaf()) {
|
||||
throw new ParsingException("Leaf node not expected for SubscriptionParameter");
|
||||
}
|
||||
for (PPSNode child : node.getChildren()) {
|
||||
switch (child.getName()) {
|
||||
case NODE_CREATION_DATE:
|
||||
config.subscriptionCreationTimeInMs = parseDate(getPpsNodeValue(child));
|
||||
break;
|
||||
case NODE_EXPIRATION_DATE:
|
||||
config.subscriptionExpirationTimeInMs = parseDate(getPpsNodeValue(child));
|
||||
break;
|
||||
case NODE_TYPE_OF_SUBSCRIPTION:
|
||||
config.subscriptionType = getPpsNodeValue(child);
|
||||
break;
|
||||
case NODE_USAGE_LIMITS:
|
||||
parseUsageLimits(child, config);
|
||||
break;
|
||||
default:
|
||||
throw new ParsingException("Unknown node under SubscriptionParameter"
|
||||
+ child.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse configurations under PerProviderSubscription/SubscriptionParameter/UsageLimits
|
||||
* subtree.
|
||||
*
|
||||
* @param node PPSNode representing the root of
|
||||
* PerProviderSubscription/SubscriptionParameter/UsageLimits subtree
|
||||
* @param config Instance of {@link PasspointConfiguration}
|
||||
* @throws ParsingException
|
||||
*/
|
||||
private static void parseUsageLimits(PPSNode node, PasspointConfiguration config)
|
||||
throws ParsingException {
|
||||
if (node.isLeaf()) {
|
||||
throw new ParsingException("Leaf node not expected for UsageLimits");
|
||||
}
|
||||
for (PPSNode child : node.getChildren()) {
|
||||
switch (child.getName()) {
|
||||
case NODE_DATA_LIMIT:
|
||||
config.usageLimitDataLimit = parseLong(getPpsNodeValue(child), 10);
|
||||
break;
|
||||
case NODE_START_DATE:
|
||||
config.usageLimitStartTimeInMs = parseDate(getPpsNodeValue(child));
|
||||
break;
|
||||
case NODE_TIME_LIMIT:
|
||||
config.usageLimitTimeLimitInMinutes = parseLong(getPpsNodeValue(child), 10);
|
||||
break;
|
||||
case NODE_USAGE_TIME_PERIOD:
|
||||
config.usageLimitUsageTimePeriodInMinutes =
|
||||
parseLong(getPpsNodeValue(child), 10);
|
||||
break;
|
||||
default:
|
||||
throw new ParsingException("Unknown node under UsageLimits"
|
||||
+ child.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a hex string to a byte array.
|
||||
*
|
||||
@@ -1504,6 +1617,21 @@ public final class PPSMOParser {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string representing a long integer.
|
||||
*
|
||||
* @param value String of long integer value
|
||||
* @return long
|
||||
* @throws ParsingException
|
||||
*/
|
||||
private static long parseLong(String value, int radix) throws ParsingException {
|
||||
try {
|
||||
return Long.parseLong(value, radix);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException("Invalid long integer value: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a List<Long> to a primitive long array long[].
|
||||
*
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
<DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
|
||||
</Type>
|
||||
</RTProperties>
|
||||
<Node>
|
||||
<NodeName>UpdateIdentifier</NodeName>
|
||||
<Value>12</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>i001</NodeName>
|
||||
<Node>
|
||||
@@ -297,6 +301,99 @@
|
||||
<Value>23</Value>
|
||||
</Node>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>CredentialPriority</NodeName>
|
||||
<Value>99</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>AAAServerTrustRoot</NodeName>
|
||||
<Node>
|
||||
<NodeName>a001</NodeName>
|
||||
<Node>
|
||||
<NodeName>CertURL</NodeName>
|
||||
<Value>server1.trust.root.com</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>CertSHA256Fingerprint</NodeName>
|
||||
<Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
|
||||
</Node>
|
||||
</Node>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>SubscriptionUpdate</NodeName>
|
||||
<Node>
|
||||
<NodeName>UpdateInterval</NodeName>
|
||||
<Value>120</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>UpdateMethod</NodeName>
|
||||
<Value>SSP-ClientInitiated</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>Restriction</NodeName>
|
||||
<Value>RoamingPartner</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>URI</NodeName>
|
||||
<Value>subscription.update.com</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>UsernamePassword</NodeName>
|
||||
<Node>
|
||||
<NodeName>Username</NodeName>
|
||||
<Value>subscriptionUser</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>Password</NodeName>
|
||||
<Value>subscriptionPass</Value>
|
||||
</Node>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>TrustRoot</NodeName>
|
||||
<Node>
|
||||
<NodeName>CertURL</NodeName>
|
||||
<Value>subscription.update.cert.com</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>CertSHA256Fingerprint</NodeName>
|
||||
<Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
|
||||
</Node>
|
||||
</Node>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>SubscriptionParameter</NodeName>
|
||||
<Node>
|
||||
<NodeName>CreationDate</NodeName>
|
||||
<Value>2016-02-01T10:00:00Z</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>ExpirationDate</NodeName>
|
||||
<Value>2016-03-01T10:00:00Z</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>TypeOfSubscription</NodeName>
|
||||
<Value>Gold</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>UsageLimits</NodeName>
|
||||
<Node>
|
||||
<NodeName>DataLimit</NodeName>
|
||||
<Value>921890</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>StartDate</NodeName>
|
||||
<Value>2016-12-01T10:00:00Z</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>TimeLimit</NodeName>
|
||||
<Value>120</Value>
|
||||
</Node>
|
||||
<Node>
|
||||
<NodeName>UsageTimePeriod</NodeName>
|
||||
<Value>99910</Value>
|
||||
</Node>
|
||||
</Node>
|
||||
</Node>
|
||||
</Node>
|
||||
</Node>
|
||||
</MgmtTree>
|
||||
|
||||
@@ -30,7 +30,9 @@ import android.util.Base64;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
@@ -38,6 +40,8 @@ import java.util.HashMap;
|
||||
*/
|
||||
@SmallTest
|
||||
public class PasspointConfigurationTest {
|
||||
private static final int MAX_URL_BYTES = 1023;
|
||||
private static final int CERTIFICATE_FINGERPRINT_BYTES = 32;
|
||||
|
||||
/**
|
||||
* Utility function for creating a {@link android.net.wifi.hotspot2.pps.HomeSP}.
|
||||
@@ -111,11 +115,25 @@ public class PasspointConfigurationTest {
|
||||
policy.policyUpdate.base64EncodedPassword =
|
||||
Base64.encodeToString("password".getBytes(), Base64.DEFAULT);
|
||||
policy.policyUpdate.trustRootCertUrl = "trust.cert.com";
|
||||
policy.policyUpdate.trustRootCertSha256Fingerprint = new byte[32];
|
||||
policy.policyUpdate.trustRootCertSha256Fingerprint =
|
||||
new byte[CERTIFICATE_FINGERPRINT_BYTES];
|
||||
|
||||
return policy;
|
||||
}
|
||||
|
||||
private static UpdateParameter createSubscriptionUpdate() {
|
||||
UpdateParameter subUpdate = new UpdateParameter();
|
||||
subUpdate.updateIntervalInMinutes = 9021;
|
||||
subUpdate.updateMethod = UpdateParameter.UPDATE_METHOD_SSP;
|
||||
subUpdate.restriction = UpdateParameter.UPDATE_RESTRICTION_ROAMING_PARTNER;
|
||||
subUpdate.serverUri = "subscription.update.com";
|
||||
subUpdate.username = "subUsername";
|
||||
subUpdate.base64EncodedPassword =
|
||||
Base64.encodeToString("subPassword".getBytes(), Base64.DEFAULT);
|
||||
subUpdate.trustRootCertUrl = "subscription.trust.cert.com";
|
||||
subUpdate.trustRootCertSha256Fingerprint = new byte[CERTIFICATE_FINGERPRINT_BYTES];
|
||||
return subUpdate;
|
||||
}
|
||||
/**
|
||||
* Helper function for creating a {@link PasspointConfiguration} for testing.
|
||||
*
|
||||
@@ -126,6 +144,21 @@ public class PasspointConfigurationTest {
|
||||
config.homeSp = createHomeSp();
|
||||
config.credential = createCredential();
|
||||
config.policy = createPolicy();
|
||||
config.subscriptionUpdate = createSubscriptionUpdate();
|
||||
config.trustRootCertList = new HashMap<>();
|
||||
config.trustRootCertList.put("trustRoot.cert1.com",
|
||||
new byte[CERTIFICATE_FINGERPRINT_BYTES]);
|
||||
config.trustRootCertList.put("trustRoot.cert2.com",
|
||||
new byte[CERTIFICATE_FINGERPRINT_BYTES]);
|
||||
config.updateIdentifier = 1;
|
||||
config.credentialPriority = 120;
|
||||
config.subscriptionCreationTimeInMs = 231200;
|
||||
config.subscriptionExpirationTimeInMs = 2134232;
|
||||
config.subscriptionType = "Gold";
|
||||
config.usageLimitUsageTimePeriodInMinutes = 3600;
|
||||
config.usageLimitStartTimeInMs = 124214213;
|
||||
config.usageLimitDataLimit = 14121;
|
||||
config.usageLimitTimeLimitInMinutes = 78912;
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -201,6 +234,31 @@ public class PasspointConfigurationTest {
|
||||
verifyParcel(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify parcel read/write for a configuration that doesn't contain subscription update.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void verifyParcelWithoutSubscriptionUpdate() throws Exception {
|
||||
PasspointConfiguration config = createConfig();
|
||||
config.subscriptionUpdate = null;
|
||||
verifyParcel(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify parcel read/write for a configuration that doesn't contain trust root certificate
|
||||
* list.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void verifyParcelWithoutTrustRootCertList() throws Exception {
|
||||
PasspointConfiguration config = createConfig();
|
||||
config.trustRootCertList = null;
|
||||
verifyParcel(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a default/empty configuration is invalid.
|
||||
*
|
||||
@@ -260,6 +318,60 @@ public class PasspointConfigurationTest {
|
||||
assertTrue(config.validate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a configuration without subscription update is valid, since subscription
|
||||
* update configurations are optional (applied for Hotspot 2.0 Release only).
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void validateConfigWithoutSubscriptionUpdate() throws Exception {
|
||||
PasspointConfiguration config = createConfig();
|
||||
config.subscriptionUpdate = null;
|
||||
assertTrue(config.validate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a configuration with a trust root certificate URL exceeding the max size
|
||||
* is invalid.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void validateConfigWithInvalidTrustRootCertUrl() throws Exception {
|
||||
PasspointConfiguration config = createConfig();
|
||||
byte[] rawUrlBytes = new byte[MAX_URL_BYTES + 1];
|
||||
Arrays.fill(rawUrlBytes, (byte) 'a');
|
||||
config.trustRootCertList.put(new String(rawUrlBytes, StandardCharsets.UTF_8),
|
||||
new byte[CERTIFICATE_FINGERPRINT_BYTES]);
|
||||
assertFalse(config.validate());
|
||||
|
||||
config.trustRootCertList = new HashMap<>();
|
||||
config.trustRootCertList.put(null, new byte[CERTIFICATE_FINGERPRINT_BYTES]);
|
||||
assertFalse(config.validate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a configuration with an invalid trust root certificate fingerprint is invalid.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void validateConfigWithInvalidTrustRootCertFingerprint() throws Exception {
|
||||
PasspointConfiguration config = createConfig();
|
||||
config.trustRootCertList = new HashMap<>();
|
||||
config.trustRootCertList.put("test.cert.com", new byte[CERTIFICATE_FINGERPRINT_BYTES + 1]);
|
||||
assertFalse(config.validate());
|
||||
|
||||
config.trustRootCertList = new HashMap<>();
|
||||
config.trustRootCertList.put("test.cert.com", new byte[CERTIFICATE_FINGERPRINT_BYTES - 1]);
|
||||
assertFalse(config.validate());
|
||||
|
||||
config.trustRootCertList = new HashMap<>();
|
||||
config.trustRootCertList.put("test.cert.com", null);
|
||||
assertFalse(config.validate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that copy constructor works when pass in a null source.
|
||||
*
|
||||
|
||||
@@ -85,7 +85,38 @@ public class PPSMOParserTest {
|
||||
* @return {@link PasspointConfiguration}
|
||||
*/
|
||||
private PasspointConfiguration generateConfigurationFromPPSMOTree() throws Exception {
|
||||
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
|
||||
PasspointConfiguration config = new PasspointConfiguration();
|
||||
config.updateIdentifier = 12;
|
||||
config.credentialPriority = 99;
|
||||
|
||||
// AAA Server trust root.
|
||||
config.trustRootCertList = new HashMap<>();
|
||||
byte[] certFingerprint = new byte[32];
|
||||
Arrays.fill(certFingerprint, (byte) 0x1f);
|
||||
config.trustRootCertList.put("server1.trust.root.com", certFingerprint);
|
||||
|
||||
// Subscription update.
|
||||
config.subscriptionUpdate = new UpdateParameter();
|
||||
config.subscriptionUpdate.updateIntervalInMinutes = 120;
|
||||
config.subscriptionUpdate.updateMethod = UpdateParameter.UPDATE_METHOD_SSP;
|
||||
config.subscriptionUpdate.restriction = UpdateParameter.UPDATE_RESTRICTION_ROAMING_PARTNER;
|
||||
config.subscriptionUpdate.serverUri = "subscription.update.com";
|
||||
config.subscriptionUpdate.username = "subscriptionUser";
|
||||
config.subscriptionUpdate.base64EncodedPassword = "subscriptionPass";
|
||||
config.subscriptionUpdate.trustRootCertUrl = "subscription.update.cert.com";
|
||||
config.subscriptionUpdate.trustRootCertSha256Fingerprint = new byte[32];
|
||||
Arrays.fill(config.subscriptionUpdate.trustRootCertSha256Fingerprint, (byte) 0x1f);
|
||||
|
||||
// Subscription parameters.
|
||||
config.subscriptionCreationTimeInMs = format.parse("2016-02-01T10:00:00Z").getTime();
|
||||
config.subscriptionExpirationTimeInMs = format.parse("2016-03-01T10:00:00Z").getTime();
|
||||
config.subscriptionType = "Gold";
|
||||
config.usageLimitDataLimit = 921890;
|
||||
config.usageLimitStartTimeInMs = format.parse("2016-12-01T10:00:00Z").getTime();
|
||||
config.usageLimitTimeLimitInMinutes = 120;
|
||||
config.usageLimitUsageTimePeriodInMinutes = 99910;
|
||||
|
||||
// HomeSP configuration.
|
||||
config.homeSp = new HomeSP();
|
||||
@@ -101,7 +132,6 @@ public class PPSMOParserTest {
|
||||
config.homeSp.otherHomePartners = new String[] {"other.fqdn.com"};
|
||||
|
||||
// Credential configuration.
|
||||
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
config.credential = new Credential();
|
||||
config.credential.creationTimeInMs = format.parse("2016-01-01T10:00:00Z").getTime();
|
||||
config.credential.expirationTimeInMs = format.parse("2016-02-01T10:00:00Z").getTime();
|
||||
@@ -161,8 +191,7 @@ public class PPSMOParserTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and verify all supported fields under PPS MO tree (currently only fields under
|
||||
* HomeSP and Credential subtree).
|
||||
* Parse and verify all supported fields under PPS MO tree.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user