diff options
| author | 2017-01-18 16:00:45 -0800 | |
|---|---|---|
| committer | 2017-01-25 10:20:22 -0800 | |
| commit | ddf6fa06c62e60c103a8f184688ee70a40737d62 (patch) | |
| tree | 900753882ac82cf58c9a33ec68b83d4a5022af58 | |
| parent | 2d7af45e939a49f3b75e275a12824c9be3d2a064 (diff) | |
hotspot2: added remaining parameters to PasspointConfiguration
Added remaining parameters to PasspointConfiguration to support complete
PerProviderSubscription tree for Release 2 support.
The new parameters include:
- Update identifier
- Credential priority
- AAA server trust root
- Subscription update
- Subscription parameters
Bug: 34198926
Test: frameworks/base/wifi/test/runtests.sh
Change-Id: If50253612f7777b3b693a344378a2e4810b3ff47
5 files changed, 672 insertions, 57 deletions
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java index ad7477ca230d..ca4d12141871 100644 --- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java +++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java @@ -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,11 +41,107 @@ 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. */ public PasspointConfiguration() {} @@ -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; + } } diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java index e557c64e17d6..22b0f977d3e8 100644 --- a/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java +++ b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java @@ -133,6 +133,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. */ private static final String NODE_HOMESP = "HomeSP"; @@ -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"); @@ -1450,6 +1472,97 @@ public final class PPSMOParser { } /** + * 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. * * @param str String containing hex values @@ -1505,6 +1618,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[]. * * @param list List to be converted diff --git a/wifi/tests/assets/pps/PerProviderSubscription.xml b/wifi/tests/assets/pps/PerProviderSubscription.xml index 7f36cdbf5cd6..7f2d95de95e9 100644 --- a/wifi/tests/assets/pps/PerProviderSubscription.xml +++ b/wifi/tests/assets/pps/PerProviderSubscription.xml @@ -8,6 +8,10 @@ </Type> </RTProperties> <Node> + <NodeName>UpdateIdentifier</NodeName> + <Value>12</Value> + </Node> + <Node> <NodeName>i001</NodeName> <Node> <NodeName>HomeSP</NodeName> @@ -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> diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java index a386d2a51abb..1eb08e0d3b58 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java @@ -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; } @@ -202,6 +235,31 @@ public class PasspointConfigurationTest { } /** + * 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. * * @throws Exception @@ -261,6 +319,60 @@ public class PasspointConfigurationTest { } /** + * 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. * * @throws Exception diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java index e9b41a95e6d7..055204ce5b03 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java @@ -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 */ |