hotspot2: added remaining parameters to PasspointConfiguration am: ddf6fa06c6

am: 44bcb75926

Change-Id: Ibb587721014dd25243f7423864f1d5019ee42fde
This commit is contained in:
Peter Qiu
2017-01-26 23:01:35 +00:00
committed by android-build-merger
5 changed files with 672 additions and 57 deletions

View File

@@ -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;
}
}

View File

@@ -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[].
*

View File

@@ -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>

View File

@@ -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.
*

View File

@@ -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
*/