summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Peter Qiu <zqiu@google.com> 2017-01-17 11:41:27 -0800
committer Peter Qiu <zqiu@google.com> 2017-01-25 10:14:37 -0800
commit2d7af45e939a49f3b75e275a12824c9be3d2a064 (patch)
tree947a44b9a56b07a33951bd1c56b16f00d2f85a44
parent815fb5479868ec7160bdd293766e827c576467e0 (diff)
hotspot2: add support for complete PerProviderSubscription/Policy subtree
Added Policy to PasspointConfiguration and the corresponding parser support in PPSMOParser. While there, fix a typo in node name "CertSHA256Fingerprint" under CertificateCredential. Bug: 34198926 Test: frameworks/base/wifi/test/runtests.sh Change-Id: Iabe27cd83b6658ed7d4f895d7fe2255fe2094ebb
-rw-r--r--wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java19
-rw-r--r--wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java453
-rw-r--r--wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl19
-rw-r--r--wifi/java/android/net/wifi/hotspot2/pps/Policy.java452
-rw-r--r--wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl19
-rw-r--r--wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java303
-rw-r--r--wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base642
-rw-r--r--wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf2
-rw-r--r--wifi/tests/assets/pps/PerProviderSubscription.xml140
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java134
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java39
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java304
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java348
13 files changed, 2199 insertions, 35 deletions
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 643753abf5dc..ad7477ca230d 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -18,6 +18,7 @@ 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.os.Parcelable;
import android.os.Parcel;
@@ -28,13 +29,12 @@ import android.os.Parcel;
* For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
* Release 2 Technical Specification.
*
- * Currently, only HomeSP and Credential subtrees are supported.
- *
* @hide
*/
public final class PasspointConfiguration implements Parcelable {
public HomeSP homeSp = null;
public Credential credential = null;
+ public Policy policy = null;
/**
* Constructor for creating PasspointConfiguration with default values.
@@ -54,6 +54,9 @@ public final class PasspointConfiguration implements Parcelable {
if (source.credential != null) {
credential = new Credential(source.credential);
}
+ if (source.policy != null) {
+ policy = new Policy(source.policy);
+ }
}
}
@@ -66,6 +69,7 @@ public final class PasspointConfiguration implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(homeSp, flags);
dest.writeParcelable(credential, flags);
+ dest.writeParcelable(policy, flags);
}
@Override
@@ -77,9 +81,10 @@ public final class PasspointConfiguration implements Parcelable {
return false;
}
PasspointConfiguration that = (PasspointConfiguration) thatObject;
- return (homeSp == null ? that.homeSp == null : homeSp.equals(that.homeSp)) &&
- (credential == null ? that.credential == null :
- credential.equals(that.credential));
+ 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);
}
/**
@@ -94,6 +99,9 @@ public final class PasspointConfiguration implements Parcelable {
if (credential == null || !credential.validate()) {
return false;
}
+ if (policy != null && !policy.validate()) {
+ return false;
+ }
return true;
}
@@ -104,6 +112,7 @@ public final class PasspointConfiguration implements Parcelable {
PasspointConfiguration config = new PasspointConfiguration();
config.homeSp = in.readParcelable(null);
config.credential = in.readParcelable(null);
+ config.policy = in.readParcelable(null);
return config;
}
@Override
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java
index 98fd0f3bde15..e557c64e17d6 100644
--- a/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java
@@ -19,6 +19,8 @@ package android.net.wifi.hotspot2.omadm;
import android.net.wifi.hotspot2.PasspointConfiguration;
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.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -168,13 +170,40 @@ public final class PPSMOParser {
private static final String NODE_INNER_METHOD = "InnerMethod";
private static final String NODE_DIGITAL_CERTIFICATE = "DigitalCertificate";
private static final String NODE_CERTIFICATE_TYPE = "CertificateType";
- private static final String NODE_CERT_SHA256_FINGERPRINT = "CertSHA256FingerPrint";
+ private static final String NODE_CERT_SHA256_FINGERPRINT = "CertSHA256Fingerprint";
private static final String NODE_REALM = "Realm";
private static final String NODE_SIM = "SIM";
private static final String NODE_SIM_IMSI = "IMSI";
private static final String NODE_CHECK_AAA_SERVER_CERT_STATUS = "CheckAAAServerCertStatus";
/**
+ * Fields under Policy subtree.
+ */
+ private static final String NODE_POLICY = "Policy";
+ private static final String NODE_PREFERRED_ROAMING_PARTNER_LIST =
+ "PreferredRoamingPartnerList";
+ private static final String NODE_FQDN_MATCH = "FQDN_Match";
+ private static final String NODE_PRIORITY = "Priority";
+ private static final String NODE_COUNTRY = "Country";
+ private static final String NODE_MIN_BACKHAUL_THRESHOLD = "MinBackhaulThreshold";
+ private static final String NODE_NETWORK_TYPE = "NetworkType";
+ private static final String NODE_DOWNLINK_BANDWIDTH = "DLBandwidth";
+ private static final String NODE_UPLINK_BANDWIDTH = "ULBandwidth";
+ private static final String NODE_POLICY_UPDATE = "PolicyUpdate";
+ private static final String NODE_UPDATE_INTERVAL = "UpdateInterval";
+ private static final String NODE_UPDATE_METHOD = "UpdateMethod";
+ private static final String NODE_RESTRICTION = "Restriction";
+ private static final String NODE_URI = "URI";
+ private static final String NODE_TRUST_ROOT = "TrustRoot";
+ private static final String NODE_CERT_URL = "CertURL";
+ private static final String NODE_SP_EXCLUSION_LIST = "SPExclusionList";
+ private static final String NODE_REQUIRED_PROTO_PORT_TUPLE = "RequiredProtoPortTuple";
+ private static final String NODE_IP_PROTOCOL = "IPProtocol";
+ private static final String NODE_PORT_NUMBER = "PortNumber";
+ private static final String NODE_MAXIMUM_BSS_LOAD_VALUE = "MaximumBSSLoadValue";
+ private static final String NODE_OTHER = "Other";
+
+ /**
* URN (Unique Resource Name) for PerProviderSubscription Management Object Tree.
*/
private static final String PPS_MO_URN =
@@ -551,6 +580,9 @@ public final class PPSMOParser {
case NODE_CREDENTIAL:
config.credential = parseCredential(child);
break;
+ case NODE_POLICY:
+ config.policy = parsePolicy(child);
+ break;
default:
throw new ParsingException("Unknown node: " + child.getName());
}
@@ -999,6 +1031,425 @@ public final class PPSMOParser {
}
/**
+ * Parse configurations under PerProviderSubscription/Policy subtree.
+ *
+ * @param node PPSNode representing the root of the PerProviderSubscription/Policy subtree
+ * @return {@link Policy}
+ * @throws ParsingException
+ */
+ private static Policy parsePolicy(PPSNode node) throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for Policy");
+ }
+
+ Policy policy = new Policy();
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_PREFERRED_ROAMING_PARTNER_LIST:
+ policy.preferredRoamingPartnerList = parsePreferredRoamingPartnerList(child);
+ break;
+ case NODE_MIN_BACKHAUL_THRESHOLD:
+ parseMinBackhaulThreshold(child, policy);
+ break;
+ case NODE_POLICY_UPDATE:
+ policy.policyUpdate = parseUpdateParameter(child);
+ break;
+ case NODE_SP_EXCLUSION_LIST:
+ policy.excludedSsidList = parseSpExclusionList(child);
+ break;
+ case NODE_REQUIRED_PROTO_PORT_TUPLE:
+ policy.requiredProtoPortMap = parseRequiredProtoPortTuple(child);
+ break;
+ case NODE_MAXIMUM_BSS_LOAD_VALUE:
+ policy.maximumBssLoadValue = parseInteger(getPpsNodeValue(child));
+ break;
+ default:
+ throw new ParsingException("Unknown node under Policy: " + child.getName());
+ }
+ }
+ return policy;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/PreferredRoamingPartnerList
+ * subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/PreferredRoamingPartnerList subtree
+ * @return List of {@link Policy#RoamingPartner}
+ * @throws ParsingException
+ */
+ private static List<Policy.RoamingPartner> parsePreferredRoamingPartnerList(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for PreferredRoamingPartnerList");
+ }
+ List<Policy.RoamingPartner> partnerList = new ArrayList<>();
+ for (PPSNode child : node.getChildren()) {
+ partnerList.add(parsePreferredRoamingPartner(child));
+ }
+ return partnerList;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/PreferredRoamingPartnerList/<X+>
+ * subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/PreferredRoamingPartnerList/<X+> subtree
+ * @return {@link Policy#RoamingPartner}
+ * @throws ParsingException
+ */
+ private static Policy.RoamingPartner parsePreferredRoamingPartner(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for PreferredRoamingPartner "
+ + "instance");
+ }
+
+ Policy.RoamingPartner roamingPartner = new Policy.RoamingPartner();
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_FQDN_MATCH:
+ // FQDN_Match field is in the format of "[FQDN],[MatchInfo]", where [MatchInfo]
+ // is either "exactMatch" for exact match of FQDN or "includeSubdomains" for
+ // matching all FQDNs with the same sub-domain.
+ String fqdnMatch = getPpsNodeValue(child);
+ String[] fqdnMatchArray = fqdnMatch.split(",");
+ if (fqdnMatchArray.length != 2) {
+ throw new ParsingException("Invalid FQDN_Match: " + fqdnMatch);
+ }
+ roamingPartner.fqdn = fqdnMatchArray[0];
+ if (TextUtils.equals(fqdnMatchArray[1], "exactMatch")) {
+ roamingPartner.fqdnExactMatch = true;
+ } else if (TextUtils.equals(fqdnMatchArray[1], "includeSubdomains")) {
+ roamingPartner.fqdnExactMatch = false;
+ } else {
+ throw new ParsingException("Invalid FQDN_Match: " + fqdnMatch);
+ }
+ break;
+ case NODE_PRIORITY:
+ roamingPartner.priority = parseInteger(getPpsNodeValue(child));
+ break;
+ case NODE_COUNTRY:
+ roamingPartner.countries = getPpsNodeValue(child);
+ break;
+ default:
+ throw new ParsingException("Unknown node under PreferredRoamingPartnerList "
+ + "instance " + child.getName());
+ }
+ }
+ return roamingPartner;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/MinBackhaulThreshold subtree
+ * into the given policy.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/MinBackhaulThreshold subtree
+ * @param policy The policy to store the MinBackhualThreshold configuration
+ * @throws ParsingException
+ */
+ private static void parseMinBackhaulThreshold(PPSNode node, Policy policy)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for MinBackhaulThreshold");
+ }
+ for (PPSNode child : node.getChildren()) {
+ parseMinBackhaulThresholdInstance(child, policy);
+ }
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/MinBackhaulThreshold/<X+> subtree
+ * into the given policy.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/MinBackhaulThreshold/<X+> subtree
+ * @param policy The policy to store the MinBackhaulThreshold configuration
+ * @throws ParsingException
+ */
+ private static void parseMinBackhaulThresholdInstance(PPSNode node, Policy policy)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for MinBackhaulThreshold instance");
+ }
+ String networkType = null;
+ long downlinkBandwidth = Long.MIN_VALUE;
+ long uplinkBandwidth = Long.MIN_VALUE;
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_NETWORK_TYPE:
+ 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));
+ }
+ break;
+ case NODE_UPLINK_BANDWIDTH:
+ try {
+ uplinkBandwidth = Long.parseLong(getPpsNodeValue(child));
+ } catch (NumberFormatException e) {
+ throw new ParsingException("Invalid value for downlink bandwidth: "
+ + getPpsNodeValue(child));
+ }
+ break;
+ default:
+ throw new ParsingException("Unknown node under MinBackhaulThreshold instance "
+ + child.getName());
+ }
+ }
+ if (networkType == null) {
+ throw new ParsingException("Missing NetworkType field");
+ }
+
+ if (TextUtils.equals(networkType, "home")) {
+ policy.minHomeDownlinkBandwidth = downlinkBandwidth;
+ policy.minHomeUplinkBandwidth = uplinkBandwidth;
+ } else if (TextUtils.equals(networkType, "roaming")) {
+ policy.minRoamingDownlinkBandwidth = downlinkBandwidth;
+ policy.minRoamingUplinkBandwidth = uplinkBandwidth;
+ } else {
+ throw new ParsingException("Invalid network type: " + networkType);
+ }
+ }
+
+ /**
+ * Parse update parameters. This contained configurations from either
+ * PerProviderSubscription/Policy/PolicyUpdate or PerProviderSubscription/SubscriptionUpdate
+ * subtree.
+ *
+ * @param node PPSNode representing the root of the PerProviderSubscription/Policy/PolicyUpdate
+ * or PerProviderSubscription/SubscriptionUpdate subtree
+ * @return {@link UpdateParameter}
+ * @throws ParsingException
+ */
+ private static UpdateParameter parseUpdateParameter(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for Update Parameters");
+ }
+
+ UpdateParameter updateParam = new UpdateParameter();
+ 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));
+ }
+ break;
+ case NODE_UPDATE_METHOD:
+ updateParam.updateMethod = getPpsNodeValue(child);
+ break;
+ case NODE_RESTRICTION:
+ updateParam.restriction = getPpsNodeValue(child);
+ break;
+ case NODE_URI:
+ updateParam.serverUri = getPpsNodeValue(child);
+ break;
+ case NODE_USERNAME_PASSWORD:
+ Pair<String, String> usernamePassword = parseUpdateUserCredential(child);
+ updateParam.username = usernamePassword.first;
+ updateParam.base64EncodedPassword = usernamePassword.second;
+ break;
+ case NODE_TRUST_ROOT:
+ Pair<String, byte[]> trustRoot = parseUpdateTrustRoot(child);
+ updateParam.trustRootCertUrl = trustRoot.first;
+ updateParam.trustRootCertSha256Fingerprint = trustRoot.second;
+ break;
+ case NODE_OTHER:
+ Log.d(TAG, "Ignore unsupported paramter: " + child.getName());
+ break;
+ default:
+ throw new ParsingException("Unknown node under Update Parameters: "
+ + child.getName());
+ }
+ }
+ return updateParam;
+ }
+
+ /**
+ * Parse username and password parameters associated with policy or subscription update.
+ * This contained configurations under either
+ * PerProviderSubscription/Policy/PolicyUpdate/UsernamePassword or
+ * PerProviderSubscription/SubscriptionUpdate/UsernamePassword subtree.
+ *
+ * @param node PPSNode representing the root of the UsernamePassword subtree
+ * @return Pair of username and password
+ * @throws ParsingException
+ */
+ private static Pair<String, String> parseUpdateUserCredential(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for UsernamePassword");
+ }
+
+ String username = null;
+ String password = null;
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_USERNAME:
+ username = getPpsNodeValue(child);
+ break;
+ case NODE_PASSWORD:
+ password = getPpsNodeValue(child);
+ break;
+ default:
+ throw new ParsingException("Unknown node under UsernamePassword: "
+ + child.getName());
+ }
+ }
+ return Pair.create(username, password);
+ }
+
+ /**
+ * Parse the trust root parameters associated with policy or subscription update.
+ * This contained configurations under either
+ * PerProviderSubscription/Policy/PolicyUpdate/TrustRoot or
+ * PerProviderSubscription/SubscriptionUpdate/TrustRoot 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)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for TrustRoot");
+ }
+
+ String certUrl = null;
+ byte[] certFingerprint = null;
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_CERT_URL:
+ certUrl = getPpsNodeValue(child);
+ break;
+ case NODE_CERT_SHA256_FINGERPRINT:
+ certFingerprint = parseHexString(getPpsNodeValue(child));
+ break;
+ default:
+ throw new ParsingException("Unknown node under TrustRoot: "
+ + child.getName());
+ }
+ }
+ return Pair.create(certUrl, certFingerprint);
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/SPExclusionList subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/SPExclusionList subtree
+ * @return Array of excluded SSIDs
+ * @throws ParsingException
+ */
+ private static String[] parseSpExclusionList(PPSNode node) throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for SPExclusionList");
+ }
+ List<String> ssidList = new ArrayList<>();
+ for (PPSNode child : node.getChildren()) {
+ ssidList.add(parseSpExclusionInstance(child));
+ }
+ return ssidList.toArray(new String[ssidList.size()]);
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/SPExclusionList/<X+> subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/SPExclusionList/<X+> subtree
+ * @return String
+ * @throws ParsingException
+ */
+ private static String parseSpExclusionInstance(PPSNode node) throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for SPExclusion instance");
+ }
+ String ssid = null;
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_SSID:
+ ssid = getPpsNodeValue(child);
+ break;
+ default:
+ throw new ParsingException("Unknown node under SPExclusion instance");
+ }
+ }
+ return ssid;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/RequiredProtoPortTuple subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/RequiredProtoPortTuple subtree
+ * @return Map of IP Protocol to Port Number tuples
+ * @throws ParsingException
+ */
+ private static Map<Integer, String> parseRequiredProtoPortTuple(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for RequiredProtoPortTuple");
+ }
+ Map<Integer, String> protoPortTupleMap = new HashMap<>();
+ for (PPSNode child : node.getChildren()) {
+ Pair<Integer, String> protoPortTuple = parseProtoPortTuple(child);
+ protoPortTupleMap.put(protoPortTuple.first, protoPortTuple.second);
+ }
+ return protoPortTupleMap;
+ }
+
+ /**
+ * Parse configurations under PerProviderSubscription/Policy/RequiredProtoPortTuple/<X+>
+ * subtree.
+ *
+ * @param node PPSNode representing the root of the
+ * PerProviderSubscription/Policy/RequiredProtoPortTuple/<X+> subtree
+ * @return Pair of IP Protocol to Port Number tuple
+ * @throws ParsingException
+ */
+ private static Pair<Integer, String> parseProtoPortTuple(PPSNode node)
+ throws ParsingException {
+ if (node.isLeaf()) {
+ throw new ParsingException("Leaf node not expected for RequiredProtoPortTuple "
+ + "instance");
+ }
+ int proto = Integer.MIN_VALUE;
+ String ports = null;
+ for (PPSNode child : node.getChildren()) {
+ switch (child.getName()) {
+ case NODE_IP_PROTOCOL:
+ proto = parseInteger(getPpsNodeValue(child));
+ break;
+ case NODE_PORT_NUMBER:
+ ports = getPpsNodeValue(child);
+ break;
+ default:
+ throw new ParsingException("Unknown node under RequiredProtoPortTuple instance"
+ + child.getName());
+ }
+ }
+ if (proto == Integer.MIN_VALUE) {
+ throw new ParsingException("Missing IPProtocol field");
+ }
+ if (ports == null) {
+ throw new ParsingException("Missing PortNumber field");
+ }
+ return Pair.create(proto, ports);
+ }
+
+ /**
* Convert a hex string to a byte array.
*
* @param str String containing hex values
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl b/wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl
new file mode 100644
index 000000000000..e923f1f0fee8
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2.pps;
+
+parcelable Policy;
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Policy.java b/wifi/java/android/net/wifi/hotspot2/pps/Policy.java
new file mode 100644
index 000000000000..b2583d324420
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Policy.java
@@ -0,0 +1,452 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2.pps;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Class representing Policy subtree in PerProviderSubscription (PPS)
+ * Management Object (MO) tree.
+ *
+ * The Policy specifies additional criteria for Passpoint network selections, such as preferred
+ * roaming partner, minimum backhaul bandwidth, and etc. It also provides the meta data for
+ * updating the policy.
+ *
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * @hide
+ */
+public final class Policy implements Parcelable {
+ private static final String TAG = "Policy";
+
+ /**
+ * Default priority for preferred roaming partner.
+ */
+ public static final int PREFERRED_ROAMING_PARTNER_DEFAULT_PRIORITY = 128;
+
+ /**
+ * Maximum number of SSIDs in the exclusion list.
+ */
+ private static final int MAX_EXCLUSION_SSIDS = 128;
+
+ /**
+ * Maximum byte for SSID.
+ */
+ private static final int MAX_SSID_BYTES = 32;
+
+ /**
+ * Maximum bytes for port string in {@link #requiredProtoPortMap}.
+ */
+ private static final int MAX_PORT_STRING_BYTES = 64;
+
+ /**
+ * Integer value used for indicating null value in the Parcel.
+ */
+ private static final int NULL_VALUE = -1;
+
+ /**
+ * Minimum available downlink/uplink bandwidth (in kilobits per second) required when
+ * selecting a network from home providers.
+ *
+ * The bandwidth is calculated as the LinkSpeed * (1 – LinkLoad/255), where LinkSpeed
+ * and LinkLoad parameters are drawn from the WAN Metrics ANQP element at that hotspot.
+ *
+ * Using Long.MIN_VALUE to indicate unset value.
+ */
+ public long minHomeDownlinkBandwidth = Long.MIN_VALUE;
+ public long minHomeUplinkBandwidth = Long.MIN_VALUE;
+
+ /**
+ * Minimum available downlink/uplink bandwidth (in kilobits per second) required when
+ * selecting a network from roaming providers.
+ *
+ * The bandwidth is calculated as the LinkSpeed * (1 – LinkLoad/255), where LinkSpeed
+ * and LinkLoad parameters are drawn from the WAN Metrics ANQP element at that hotspot.
+ *
+ * Using Long.MIN_VALUE to indicate unset value.
+ */
+ public long minRoamingDownlinkBandwidth = Long.MIN_VALUE;
+ public long minRoamingUplinkBandwidth = Long.MIN_VALUE;
+
+ /**
+ * List of SSIDs that are not preferred by the Home SP.
+ */
+ public String[] excludedSsidList = null;
+
+ /**
+ * List of IP protocol and port number required by one or more operator supported application.
+ * The port string contained one or more port numbers delimited by ",".
+ */
+ public Map<Integer, String> requiredProtoPortMap = null;
+
+ /**
+ * This specifies the maximum acceptable BSS load policy. This is used to prevent device
+ * from joining an AP whose channel is overly congested with traffic.
+ * Using Integer.MIN_VALUE to indicate unset value.
+ */
+ public int maximumBssLoadValue = Integer.MIN_VALUE;
+
+ /**
+ * Policy associated with a roaming provider. This specifies a priority associated
+ * with a roaming provider for given list of countries.
+ *
+ * Contains field under PerProviderSubscription/Policy/PreferredRoamingPartnerList.
+ */
+ public static final class RoamingPartner implements Parcelable {
+ /**
+ * FQDN of the roaming partner.
+ */
+ public String fqdn = null;
+
+ /**
+ * Flag indicating the exact match of FQDN is required for FQDN matching.
+ *
+ * When this flag is set to false, sub-domain matching is used. For example, when
+ * {@link #fqdn} s set to "example.com", "host.example.com" would be a match.
+ */
+ public boolean fqdnExactMatch = false;
+
+ /**
+ * Priority associated with this roaming partner policy.
+ */
+ public int priority = PREFERRED_ROAMING_PARTNER_DEFAULT_PRIORITY;
+
+ /**
+ * A string contained One or more, comma delimited (i.e., ",") ISO/IEC 3166-1 two
+ * character country strings or the country-independent value, "*".
+ */
+ public String countries = null;
+
+ public RoamingPartner() {}
+
+ public RoamingPartner(RoamingPartner source) {
+ if (source != null) {
+ fqdn = source.fqdn;
+ fqdnExactMatch = source.fqdnExactMatch;
+ priority = source.priority;
+ countries = source.countries;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(fqdn);
+ dest.writeInt(fqdnExactMatch ? 1 : 0);
+ dest.writeInt(priority);
+ dest.writeString(countries);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof RoamingPartner)) {
+ return false;
+ }
+
+ RoamingPartner that = (RoamingPartner) thatObject;
+ return TextUtils.equals(fqdn, that.fqdn)
+ && fqdnExactMatch == that.fqdnExactMatch
+ && priority == that.priority
+ && TextUtils.equals(countries, that.countries);
+ }
+
+ /**
+ * Validate RoamingParnter data.
+ *
+ * @return true on success
+ */
+ public boolean validate() {
+ if (TextUtils.isEmpty(fqdn)) {
+ Log.d(TAG, "Missing FQDN");
+ return false;
+ }
+ if (TextUtils.isEmpty(countries)) {
+ Log.d(TAG, "Missing countries");
+ return false;
+ }
+ return true;
+ }
+
+ public static final Creator<RoamingPartner> CREATOR =
+ new Creator<RoamingPartner>() {
+ @Override
+ public RoamingPartner createFromParcel(Parcel in) {
+ RoamingPartner roamingPartner = new RoamingPartner();
+ roamingPartner.fqdn = in.readString();
+ roamingPartner.fqdnExactMatch = in.readInt() != 0;
+ roamingPartner.priority = in.readInt();
+ roamingPartner.countries = in.readString();
+ return roamingPartner;
+ }
+
+ @Override
+ public RoamingPartner[] newArray(int size) {
+ return new RoamingPartner[size];
+ }
+ };
+ }
+ public List<RoamingPartner> preferredRoamingPartnerList = null;
+
+ /**
+ * Meta data used for policy update.
+ */
+ public UpdateParameter policyUpdate = null;
+
+ /**
+ * Constructor for creating Policy with default values.
+ */
+ public Policy() {}
+
+ /**
+ * Copy constructor.
+ *
+ * @param source The source to copy from
+ */
+ public Policy(Policy source) {
+ if (source == null) {
+ return;
+ }
+ minHomeDownlinkBandwidth = source.minHomeDownlinkBandwidth;
+ minHomeUplinkBandwidth = source.minHomeUplinkBandwidth;
+ minRoamingDownlinkBandwidth = source.minRoamingDownlinkBandwidth;
+ minRoamingUplinkBandwidth = source.minRoamingUplinkBandwidth;
+ maximumBssLoadValue = source.maximumBssLoadValue;
+ if (source.excludedSsidList != null) {
+ excludedSsidList = Arrays.copyOf(source.excludedSsidList,
+ source.excludedSsidList.length);
+ }
+ if (source.requiredProtoPortMap != null) {
+ requiredProtoPortMap = Collections.unmodifiableMap(source.requiredProtoPortMap);
+ }
+ if (source.preferredRoamingPartnerList != null) {
+ preferredRoamingPartnerList = Collections.unmodifiableList(
+ source.preferredRoamingPartnerList);
+ }
+ if (source.policyUpdate != null) {
+ policyUpdate = new UpdateParameter(source.policyUpdate);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(minHomeDownlinkBandwidth);
+ dest.writeLong(minHomeUplinkBandwidth);
+ dest.writeLong(minRoamingDownlinkBandwidth);
+ dest.writeLong(minRoamingUplinkBandwidth);
+ dest.writeStringArray(excludedSsidList);
+ writeProtoPortMap(dest, requiredProtoPortMap);
+ dest.writeInt(maximumBssLoadValue);
+ writeRoamingPartnerList(dest, flags, preferredRoamingPartnerList);
+ dest.writeParcelable(policyUpdate, flags);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof Policy)) {
+ return false;
+ }
+ Policy that = (Policy) thatObject;
+
+ return minHomeDownlinkBandwidth == that.minHomeDownlinkBandwidth
+ && minHomeUplinkBandwidth == that.minHomeUplinkBandwidth
+ && minRoamingDownlinkBandwidth == that.minRoamingDownlinkBandwidth
+ && minRoamingUplinkBandwidth == that.minRoamingUplinkBandwidth
+ && Arrays.equals(excludedSsidList, that.excludedSsidList)
+ && (requiredProtoPortMap == null) ? that.requiredProtoPortMap == null
+ : requiredProtoPortMap.equals(that.requiredProtoPortMap)
+ && maximumBssLoadValue == that.maximumBssLoadValue
+ && (preferredRoamingPartnerList == null) ? that.preferredRoamingPartnerList == null
+ : preferredRoamingPartnerList.equals(that.preferredRoamingPartnerList)
+ && (policyUpdate == null) ? that.policyUpdate == null
+ : policyUpdate.equals(that.policyUpdate);
+ }
+
+ /**
+ * Validate Policy data.
+ *
+ * @return true on success
+ */
+ public boolean validate() {
+ if (policyUpdate == null) {
+ Log.d(TAG, "PolicyUpdate not specified");
+ return false;
+ }
+ if (!policyUpdate.validate()) {
+ return false;
+ }
+
+ // Validate SSID exclusion list.
+ if (excludedSsidList != null) {
+ if (excludedSsidList.length > MAX_EXCLUSION_SSIDS) {
+ Log.d(TAG, "SSID exclusion list size exceeded the max: "
+ + excludedSsidList.length);
+ return false;
+ }
+ for (String ssid : excludedSsidList) {
+ if (ssid.getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) {
+ Log.d(TAG, "Invalid SSID: " + ssid);
+ return false;
+ }
+ }
+ }
+ // Validate required protocol to port map.
+ if (requiredProtoPortMap != null) {
+ for (Map.Entry<Integer, String> entry : requiredProtoPortMap.entrySet()) {
+ String portNumber = entry.getValue();
+ if (portNumber.getBytes(StandardCharsets.UTF_8).length > MAX_PORT_STRING_BYTES) {
+ Log.d(TAG, "PortNumber string bytes exceeded the max: " + portNumber);
+ return false;
+ }
+ }
+ }
+ // Validate preferred roaming partner list.
+ if (preferredRoamingPartnerList != null) {
+ for (RoamingPartner partner : preferredRoamingPartnerList) {
+ if (!partner.validate()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public static final Creator<Policy> CREATOR =
+ new Creator<Policy>() {
+ @Override
+ public Policy createFromParcel(Parcel in) {
+ Policy policy = new Policy();
+ policy.minHomeDownlinkBandwidth = in.readLong();
+ policy.minHomeUplinkBandwidth = in.readLong();
+ policy.minRoamingDownlinkBandwidth = in.readLong();
+ policy.minRoamingUplinkBandwidth = in.readLong();
+ policy.excludedSsidList = in.createStringArray();
+ policy.requiredProtoPortMap = readProtoPortMap(in);
+ policy.maximumBssLoadValue = in.readInt();
+ policy.preferredRoamingPartnerList = readRoamingPartnerList(in);
+ policy.policyUpdate = in.readParcelable(null);
+ return policy;
+ }
+
+ @Override
+ public Policy[] newArray(int size) {
+ return new Policy[size];
+ }
+
+ /**
+ * Helper function for reading IP Protocol to Port Number map from a Parcel.
+ *
+ * @param in The Parcel to read from
+ * @return Map of IP protocol to port number
+ */
+ private Map<Integer, String> readProtoPortMap(Parcel in) {
+ int size = in.readInt();
+ if (size == NULL_VALUE) {
+ return null;
+ }
+ Map<Integer, String> protoPortMap = new HashMap<>(size);
+ for (int i = 0; i < size; i++) {
+ int key = in.readInt();
+ String value = in.readString();
+ protoPortMap.put(key, value);
+ }
+ return protoPortMap;
+ }
+
+ /**
+ * Helper function for reading roaming partner list from a Parcel.
+ *
+ * @param in The Parcel to read from
+ * @return List of roaming partners
+ */
+ private List<RoamingPartner> readRoamingPartnerList(Parcel in) {
+ int size = in.readInt();
+ if (size == NULL_VALUE) {
+ return null;
+ }
+ List<RoamingPartner> partnerList = new ArrayList<>();
+ for (int i = 0; i < size; i++) {
+ partnerList.add(in.readParcelable(null));
+ }
+ return partnerList;
+ }
+
+ };
+
+ /**
+ * Helper function for writing IP Protocol to Port Number map to a Parcel.
+ *
+ * @param dest The Parcel to write to
+ * @param protoPortMap The map to write
+ */
+ private static void writeProtoPortMap(Parcel dest, Map<Integer, String> protoPortMap) {
+ if (protoPortMap == null) {
+ dest.writeInt(NULL_VALUE);
+ return;
+ }
+ dest.writeInt(protoPortMap.size());
+ for (Map.Entry<Integer, String> entry : protoPortMap.entrySet()) {
+ dest.writeInt(entry.getKey());
+ dest.writeString(entry.getValue());
+ }
+ }
+
+ /**
+ * Helper function for writing roaming partner list to a Parcel.
+ *
+ * @param dest The Parcel to write to
+ * @param flags The flag about how the object should be written
+ * @param partnerList The partner list to write
+ */
+ private static void writeRoamingPartnerList(Parcel dest, int flags,
+ List<RoamingPartner> partnerList) {
+ if (partnerList == null) {
+ dest.writeInt(NULL_VALUE);
+ return;
+ }
+ dest.writeInt(partnerList.size());
+ for (RoamingPartner partner : partnerList) {
+ dest.writeParcelable(partner, flags);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl
new file mode 100644
index 000000000000..701db479076a
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2.pps;
+
+parcelable UpdateParameter;
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java
new file mode 100644
index 000000000000..a390df72f061
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java
@@ -0,0 +1,303 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2.pps;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+/**
+ * Class representing configuration parameters for subscription or policy update in
+ * PerProviderSubscription (PPS) Management Object (MO) tree. This is used by both
+ * PerProviderSubscription/Policy/PolicyUpdate and PerProviderSubscription/SubscriptionUpdate
+ * subtree.
+ *
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * @hide
+ */
+public final class UpdateParameter implements Parcelable {
+ private static final String TAG = "UpdateParameter";
+
+ /**
+ * Value indicating policy update is not applicable. Thus, never check with policy server
+ * for updates.
+ */
+ public static final long UPDATE_CHECK_INTERVAL_NEVER = 0xFFFFFFFFL;
+
+ /**
+ * Valid string for UpdateMethod.
+ */
+ public static final String UPDATE_METHOD_OMADM = "OMA-DM-ClientInitiated";
+ public static final String UPDATE_METHOD_SSP = "SSP-ClientInitiated";
+
+ /**
+ * Valid string for Restriction.
+ */
+ public static final String UPDATE_RESTRICTION_HOMESP = "HomeSP";
+ public static final String UPDATE_RESTRICTION_ROAMING_PARTNER = "RoamingPartner";
+ public static final String UPDATE_RESTRICTION_UNRESTRICTED = "Unrestricted";
+
+ /**
+ * Maximum bytes for URI string.
+ */
+ private static final int MAX_URI_BYTES = 1023;
+
+ /**
+ * Maximum bytes for URI string.
+ */
+ private static final int MAX_URL_BYTES = 1023;
+
+ /**
+ * Maximum bytes for username.
+ */
+ private static final int MAX_USERNAME_BYTES = 63;
+
+ /**
+ * Maximum bytes for password.
+ */
+ private static final int MAX_PASSWORD_BYTES = 255;
+
+ /**
+ * Number of bytes for certificate SHA-256 fingerprint byte array.
+ */
+ private static final int CERTIFICATE_SHA256_BYTES = 32;
+
+ /**
+ * This specifies how often the mobile device shall check with policy server for updates.
+ *
+ * Using Long.MIN_VALUE to indicate unset value.
+ */
+ public long updateIntervalInMinutes = Long.MIN_VALUE;
+
+ /**
+ * The method used to update the policy. Permitted values are "OMA-DM-ClientInitiated"
+ * and "SPP-ClientInitiated".
+ */
+ public String updateMethod = null;
+
+ /**
+ * This specifies the hotspots at which the subscription update is permitted. Permitted
+ * values are "HomeSP", "RoamingPartner", or "Unrestricted";
+ */
+ public String restriction = null;
+
+ /**
+ * The URI of the update server.
+ */
+ public String serverUri = null;
+
+ /**
+ * Username used to authenticate with the policy server.
+ */
+ public String username = null;
+
+ /**
+ * Base64 encoded password used to authenticate with the policy server.
+ */
+ public String base64EncodedPassword = null;
+
+ /**
+ * HTTPS URL for retrieving certificate for trust root. The trust root is used to validate
+ * policy server's identity.
+ */
+ public String trustRootCertUrl = null;
+
+ /**
+ * SHA-256 fingerprint of the certificate located at {@link #trustRootCertUrl}
+ */
+ public byte[] trustRootCertSha256Fingerprint = null;
+
+ /**
+ * Constructor for creating Policy with default values.
+ */
+ public UpdateParameter() {}
+
+ /**
+ * Copy constructor.
+ *
+ * @param source The source to copy from
+ */
+ public UpdateParameter(UpdateParameter source) {
+ if (source == null) {
+ return;
+ }
+ updateIntervalInMinutes = source.updateIntervalInMinutes;
+ updateMethod = source.updateMethod;
+ restriction = source.restriction;
+ serverUri = source.serverUri;
+ username = source.username;
+ base64EncodedPassword = source.base64EncodedPassword;
+ trustRootCertUrl = source.trustRootCertUrl;
+ if (source.trustRootCertSha256Fingerprint != null) {
+ trustRootCertSha256Fingerprint = Arrays.copyOf(source.trustRootCertSha256Fingerprint,
+ source.trustRootCertSha256Fingerprint.length);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(updateIntervalInMinutes);
+ dest.writeString(updateMethod);
+ dest.writeString(restriction);
+ dest.writeString(serverUri);
+ dest.writeString(username);
+ dest.writeString(base64EncodedPassword);
+ dest.writeString(trustRootCertUrl);
+ dest.writeByteArray(trustRootCertSha256Fingerprint);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof UpdateParameter)) {
+ return false;
+ }
+ UpdateParameter that = (UpdateParameter) thatObject;
+
+ return updateIntervalInMinutes == that.updateIntervalInMinutes
+ && TextUtils.equals(updateMethod, that.updateMethod)
+ && TextUtils.equals(restriction, that.restriction)
+ && TextUtils.equals(serverUri, that.serverUri)
+ && TextUtils.equals(username, that.username)
+ && TextUtils.equals(base64EncodedPassword, that.base64EncodedPassword)
+ && TextUtils.equals(trustRootCertUrl, that.trustRootCertUrl)
+ && Arrays.equals(trustRootCertSha256Fingerprint,
+ that.trustRootCertSha256Fingerprint);
+ }
+
+ /**
+ * Validate UpdateParameter data.
+ *
+ * @return true on success
+ */
+ public boolean validate() {
+ if (updateIntervalInMinutes == Long.MIN_VALUE) {
+ Log.d(TAG, "Update interval not specified");
+ return false;
+ }
+ // Update not applicable.
+ if (updateIntervalInMinutes == UPDATE_CHECK_INTERVAL_NEVER) {
+ return true;
+ }
+
+ if (!TextUtils.equals(updateMethod, UPDATE_METHOD_OMADM)
+ && !TextUtils.equals(updateMethod, UPDATE_METHOD_SSP)) {
+ Log.d(TAG, "Unknown update method: " + updateMethod);
+ return false;
+ }
+
+ if (!TextUtils.equals(restriction, UPDATE_RESTRICTION_HOMESP)
+ && !TextUtils.equals(restriction, UPDATE_RESTRICTION_ROAMING_PARTNER)
+ && !TextUtils.equals(restriction, UPDATE_RESTRICTION_UNRESTRICTED)) {
+ Log.d(TAG, "Unknown restriction: " + restriction);
+ return false;
+ }
+
+ if (TextUtils.isEmpty(serverUri)) {
+ Log.d(TAG, "Missing update server URI");
+ return false;
+ }
+ if (serverUri.getBytes(StandardCharsets.UTF_8).length > MAX_URI_BYTES) {
+ Log.d(TAG, "URI bytes exceeded the max: "
+ + serverUri.getBytes(StandardCharsets.UTF_8).length);
+ return false;
+ }
+
+ if (TextUtils.isEmpty(username)) {
+ Log.d(TAG, "Missing username");
+ return false;
+ }
+ if (username.getBytes(StandardCharsets.UTF_8).length > MAX_USERNAME_BYTES) {
+ Log.d(TAG, "Username bytes exceeded the max: "
+ + username.getBytes(StandardCharsets.UTF_8).length);
+ return false;
+ }
+
+ if (TextUtils.isEmpty(base64EncodedPassword)) {
+ Log.d(TAG, "Missing username");
+ return false;
+ }
+ if (base64EncodedPassword.getBytes(StandardCharsets.UTF_8).length > MAX_PASSWORD_BYTES) {
+ Log.d(TAG, "Password bytes exceeded the max: "
+ + base64EncodedPassword.getBytes(StandardCharsets.UTF_8).length);
+ return false;
+ }
+ try {
+ Base64.decode(base64EncodedPassword, Base64.DEFAULT);
+ } catch (IllegalArgumentException e) {
+ Log.d(TAG, "Invalid encoding for password: " + base64EncodedPassword);
+ return false;
+ }
+
+ if (TextUtils.isEmpty(trustRootCertUrl)) {
+ Log.d(TAG, "Missing trust root certificate URL");
+ return false;
+ }
+ if (trustRootCertUrl.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) {
+ Log.d(TAG, "Trust root cert URL bytes exceeded the max: "
+ + trustRootCertUrl.getBytes(StandardCharsets.UTF_8).length);
+ return false;
+ }
+
+ if (trustRootCertSha256Fingerprint == null) {
+ Log.d(TAG, "Missing trust root certificate SHA-256 fingerprint");
+ return false;
+ }
+ if (trustRootCertSha256Fingerprint.length != CERTIFICATE_SHA256_BYTES) {
+ Log.d(TAG, "Incorrect size of trust root certificate SHA-256 fingerprint: "
+ + trustRootCertSha256Fingerprint.length);
+ return false;
+ }
+ return true;
+ }
+
+ public static final Creator<UpdateParameter> CREATOR =
+ new Creator<UpdateParameter>() {
+ @Override
+ public UpdateParameter createFromParcel(Parcel in) {
+ UpdateParameter updateParam = new UpdateParameter();
+ updateParam.updateIntervalInMinutes = in.readLong();
+ updateParam.updateMethod = in.readString();
+ updateParam.restriction = in.readString();
+ updateParam.serverUri = in.readString();
+ updateParam.username = in.readString();
+ updateParam.base64EncodedPassword = in.readString();
+ updateParam.trustRootCertUrl = in.readString();
+ updateParam.trustRootCertSha256Fingerprint = in.createByteArray();
+ return updateParam;
+ }
+
+ @Override
+ public UpdateParameter[] newArray(int size) {
+ return new UpdateParameter[size];
+ }
+ };
+}
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
index 8c1eb0867298..995963d2b4cc 100644
--- a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
@@ -42,7 +42,7 @@ V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ
a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD
QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q
-VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
+VTJSbWx1WjJWeWNISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG
bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB
OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
index 6d86dd53eb76..3ddd09f91ff4 100644
--- a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
@@ -35,7 +35,7 @@ ICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkRpZ2l0YWxDZXJ0
aWZpY2F0ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1l
PkNlcnRpZmljYXRlVHlwZTwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT54NTA5djM8L1Zh
bHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2Rl
-TmFtZT5DZXJ0U0hBMjU2RmluZ2VyUHJpbnQ8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+
+TmFtZT5DZXJ0U0hBMjU2RmluZ2VycHJpbnQ8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+
MWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYx
ZjFmMWYxZjwvVmFsdWU+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgPC9Ob2RlPgogICAgICAg
IDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlNJTTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9k
diff --git a/wifi/tests/assets/pps/PerProviderSubscription.xml b/wifi/tests/assets/pps/PerProviderSubscription.xml
index 3969f697a325..7f36cdbf5cd6 100644
--- a/wifi/tests/assets/pps/PerProviderSubscription.xml
+++ b/wifi/tests/assets/pps/PerProviderSubscription.xml
@@ -143,7 +143,7 @@
<Value>x509v3</Value>
</Node>
<Node>
- <NodeName>CertSHA256FingerPrint</NodeName>
+ <NodeName>CertSHA256Fingerprint</NodeName>
<Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
</Node>
</Node>
@@ -159,6 +159,144 @@
</Node>
</Node>
</Node>
+ <Node>
+ <NodeName>Policy</NodeName>
+ <Node>
+ <NodeName>PreferredRoamingPartnerList</NodeName>
+ <Node>
+ <NodeName>p001</NodeName>
+ <Node>
+ <NodeName>FQDN_Match</NodeName>
+ <Value>test1.fqdn.com,exactMatch</Value>
+ </Node>
+ <Node>
+ <NodeName>Priority</NodeName>
+ <Value>127</Value>
+ </Node>
+ <Node>
+ <NodeName>Country</NodeName>
+ <Value>us,fr</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>p002</NodeName>
+ <Node>
+ <NodeName>FQDN_Match</NodeName>
+ <Value>test2.fqdn.com,includeSubdomains</Value>
+ </Node>
+ <Node>
+ <NodeName>Priority</NodeName>
+ <Value>200</Value>
+ </Node>
+ <Node>
+ <NodeName>Country</NodeName>
+ <Value>*</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>MinBackhaulThreshold</NodeName>
+ <Node>
+ <NodeName>m001</NodeName>
+ <Node>
+ <NodeName>NetworkType</NodeName>
+ <Value>home</Value>
+ </Node>
+ <Node>
+ <NodeName>DLBandwidth</NodeName>
+ <Value>23412</Value>
+ </Node>
+ <Node>
+ <NodeName>ULBandwidth</NodeName>
+ <Value>9823</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>m002</NodeName>
+ <Node>
+ <NodeName>NetworkType</NodeName>
+ <Value>roaming</Value>
+ </Node>
+ <Node>
+ <NodeName>DLBandwidth</NodeName>
+ <Value>9271</Value>
+ </Node>
+ <Node>
+ <NodeName>ULBandwidth</NodeName>
+ <Value>2315</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>PolicyUpdate</NodeName>
+ <Node>
+ <NodeName>UpdateInterval</NodeName>
+ <Value>120</Value>
+ </Node>
+ <Node>
+ <NodeName>UpdateMethod</NodeName>
+ <Value>OMA-DM-ClientInitiated</Value>
+ </Node>
+ <Node>
+ <NodeName>Restriction</NodeName>
+ <Value>HomeSP</Value>
+ </Node>
+ <Node>
+ <NodeName>URI</NodeName>
+ <Value>policy.update.com</Value>
+ </Node>
+ <Node>
+ <NodeName>UsernamePassword</NodeName>
+ <Node>
+ <NodeName>Username</NodeName>
+ <Value>updateUser</Value>
+ </Node>
+ <Node>
+ <NodeName>Password</NodeName>
+ <Value>updatePass</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>TrustRoot</NodeName>
+ <Node>
+ <NodeName>CertURL</NodeName>
+ <Value>update.cert.com</Value>
+ </Node>
+ <Node>
+ <NodeName>CertSHA256Fingerprint</NodeName>
+ <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>SPExclusionList</NodeName>
+ <Node>
+ <NodeName>s001</NodeName>
+ <Node>
+ <NodeName>SSID</NodeName>
+ <Value>excludeSSID</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>RequiredProtoPortTuple</NodeName>
+ <Node>
+ <NodeName>r001</NodeName>
+ <Node>
+ <NodeName>IPProtocol</NodeName>
+ <Value>12</Value>
+ </Node>
+ <Node>
+ <NodeName>PortNumber</NodeName>
+ <Value>34,92,234</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>MaximumBSSLoadValue</NodeName>
+ <Value>23</Value>
+ </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 2350d3201171..a386d2a51abb 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -22,11 +22,17 @@ import static org.junit.Assert.assertTrue;
import android.net.wifi.EAPConstants;
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.Parcel;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
import org.junit.Test;
+import java.util.ArrayList;
+import java.util.HashMap;
+
/**
* Unit tests for {@link android.net.wifi.hotspot2.PasspointConfiguration}.
*/
@@ -66,6 +72,64 @@ public class PasspointConfigurationTest {
}
/**
+ * Helper function for creating a {@link Policy} for testing.
+ *
+ * @return {@link Policy}
+ */
+ private static Policy createPolicy() {
+ Policy policy = new Policy();
+ policy.minHomeDownlinkBandwidth = 123;
+ policy.minHomeUplinkBandwidth = 345;
+ policy.minRoamingDownlinkBandwidth = 567;
+ policy.minRoamingUplinkBandwidth = 789;
+ policy.maximumBssLoadValue = 12;
+ policy.excludedSsidList = new String[] {"ssid1", "ssid2"};
+ policy.requiredProtoPortMap = new HashMap<>();
+ policy.requiredProtoPortMap.put(12, "23,342,123");
+ policy.requiredProtoPortMap.put(23, "789,372,1235");
+
+ policy.preferredRoamingPartnerList = new ArrayList<>();
+ Policy.RoamingPartner partner1 = new Policy.RoamingPartner();
+ partner1.fqdn = "partner1.com";
+ partner1.fqdnExactMatch = true;
+ partner1.priority = 12;
+ partner1.countries = "us,jp";
+ Policy.RoamingPartner partner2 = new Policy.RoamingPartner();
+ partner2.fqdn = "partner2.com";
+ partner2.fqdnExactMatch = false;
+ partner2.priority = 42;
+ partner2.countries = "ca,fr";
+ policy.preferredRoamingPartnerList.add(partner1);
+ policy.preferredRoamingPartnerList.add(partner2);
+
+ policy.policyUpdate = new UpdateParameter();
+ policy.policyUpdate.updateIntervalInMinutes = 1712;
+ policy.policyUpdate.updateMethod = UpdateParameter.UPDATE_METHOD_OMADM;
+ policy.policyUpdate.restriction = UpdateParameter.UPDATE_RESTRICTION_HOMESP;
+ policy.policyUpdate.serverUri = "policy.update.com";
+ policy.policyUpdate.username = "username";
+ policy.policyUpdate.base64EncodedPassword =
+ Base64.encodeToString("password".getBytes(), Base64.DEFAULT);
+ policy.policyUpdate.trustRootCertUrl = "trust.cert.com";
+ policy.policyUpdate.trustRootCertSha256Fingerprint = new byte[32];
+
+ return policy;
+ }
+
+ /**
+ * Helper function for creating a {@link PasspointConfiguration} for testing.
+ *
+ * @return {@link PasspointConfiguration}
+ */
+ private static PasspointConfiguration createConfig() {
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = createHomeSp();
+ config.credential = createCredential();
+ config.policy = createPolicy();
+ return config;
+ }
+
+ /**
* Verify parcel write and read consistency for the given configuration.
*
* @param writeConfig The configuration to verify
@@ -92,39 +156,48 @@ public class PasspointConfigurationTest {
}
/**
- * Verify parcel read/write for a configuration that contained both HomeSP and Credential.
+ * Verify parcel read/write for a configuration that contained the full configuration.
*
* @throws Exception
*/
@Test
- public void verifyParcelWithHomeSPAndCredential() throws Exception {
- PasspointConfiguration config = new PasspointConfiguration();
- config.homeSp = createHomeSp();
- config.credential = createCredential();
+ public void verifyParcelWithFullConfiguration() throws Exception {
+ verifyParcel(createConfig());
+ }
+
+ /**
+ * Verify parcel read/write for a configuration that doesn't contain HomeSP.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithoutHomeSP() throws Exception {
+ PasspointConfiguration config = createConfig();
+ config.homeSp = null;
verifyParcel(config);
}
/**
- * Verify parcel read/write for a configuration that contained only HomeSP.
+ * Verify parcel read/write for a configuration that doesn't contain Credential.
*
* @throws Exception
*/
@Test
- public void verifyParcelWithHomeSPOnly() throws Exception {
- PasspointConfiguration config = new PasspointConfiguration();
- config.homeSp = createHomeSp();
+ public void verifyParcelWithoutCredential() throws Exception {
+ PasspointConfiguration config = createConfig();
+ config.credential = null;
verifyParcel(config);
}
/**
- * Verify parcel read/write for a configuration that contained only Credential.
+ * Verify parcel read/write for a configuration that doesn't contain Policy.
*
* @throws Exception
*/
@Test
- public void verifyParcelWithCredentialOnly() throws Exception {
- PasspointConfiguration config = new PasspointConfiguration();
- config.credential = createCredential();
+ public void verifyParcelWithoutPolicy() throws Exception {
+ PasspointConfiguration config = createConfig();
+ config.policy = null;
verifyParcel(config);
}
@@ -140,39 +213,50 @@ public class PasspointConfigurationTest {
}
/**
+ * Verify that a configuration contained all fields is valid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateFullConfig() throws Exception {
+ PasspointConfiguration config = createConfig();
+ assertTrue(config.validate());
+ }
+
+ /**
* Verify that a configuration without Credential is invalid.
*
* @throws Exception
*/
@Test
public void validateConfigWithoutCredential() throws Exception {
- PasspointConfiguration config = new PasspointConfiguration();
- config.homeSp = createHomeSp();
+ PasspointConfiguration config = createConfig();
+ config.credential = null;
assertFalse(config.validate());
}
/**
- * Verify that a a configuration without HomeSP is invalid.
+ * Verify that a configuration without HomeSP is invalid.
*
* @throws Exception
*/
@Test
public void validateConfigWithoutHomeSp() throws Exception {
- PasspointConfiguration config = new PasspointConfiguration();
- config.credential = createCredential();
+ PasspointConfiguration config = createConfig();
+ config.homeSp = null;
assertFalse(config.validate());
}
/**
- * Verify a valid configuration.
+ * Verify that a configuration without Policy is valid, since Policy configurations
+ * are optional (applied for Hotspot 2.0 Release only).
*
* @throws Exception
*/
@Test
- public void validateValidConfig() throws Exception {
- PasspointConfiguration config = new PasspointConfiguration();
- config.homeSp = createHomeSp();
- config.credential = createCredential();
+ public void validateConfigWithoutPolicy() throws Exception {
+ PasspointConfiguration config = createConfig();
+ config.policy = null;
assertTrue(config.validate());
}
@@ -195,9 +279,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void validateCopyConstructorWithValidSource() throws Exception {
- PasspointConfiguration sourceConfig = new PasspointConfiguration();
- sourceConfig.homeSp = createHomeSp();
- sourceConfig.credential = createCredential();
+ PasspointConfiguration sourceConfig = createConfig();
PasspointConfiguration copyConfig = new PasspointConfiguration(sourceConfig);
assertTrue(copyConfig.equals(sourceConfig));
}
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 1c7508e7d0f5..e9b41a95e6d7 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java
@@ -23,7 +23,10 @@ import android.net.wifi.hotspot2.omadm.PPSMOParser;
import android.net.wifi.hotspot2.PasspointConfiguration;
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.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
import org.junit.Test;
@@ -33,6 +36,7 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -118,6 +122,41 @@ public class PPSMOParserTest {
config.credential.simCredential = new Credential.SimCredential();
config.credential.simCredential.imsi = "imsi";
config.credential.simCredential.eapType = 24;
+
+ // Policy configuration.
+ config.policy = new Policy();
+ config.policy.preferredRoamingPartnerList = new ArrayList<>();
+ Policy.RoamingPartner partner1 = new Policy.RoamingPartner();
+ partner1.fqdn = "test1.fqdn.com";
+ partner1.fqdnExactMatch = true;
+ partner1.priority = 127;
+ partner1.countries = "us,fr";
+ Policy.RoamingPartner partner2 = new Policy.RoamingPartner();
+ partner2.fqdn = "test2.fqdn.com";
+ partner2.fqdnExactMatch = false;
+ partner2.priority = 200;
+ partner2.countries = "*";
+ config.policy.preferredRoamingPartnerList.add(partner1);
+ config.policy.preferredRoamingPartnerList.add(partner2);
+ config.policy.minHomeDownlinkBandwidth = 23412;
+ config.policy.minHomeUplinkBandwidth = 9823;
+ config.policy.minRoamingDownlinkBandwidth = 9271;
+ config.policy.minRoamingUplinkBandwidth = 2315;
+ config.policy.excludedSsidList = new String[] {"excludeSSID"};
+ config.policy.requiredProtoPortMap = new HashMap<>();
+ config.policy.requiredProtoPortMap.put(12, "34,92,234");
+ config.policy.maximumBssLoadValue = 23;
+ config.policy.policyUpdate = new UpdateParameter();
+ config.policy.policyUpdate.updateIntervalInMinutes = 120;
+ config.policy.policyUpdate.updateMethod = UpdateParameter.UPDATE_METHOD_OMADM;
+ config.policy.policyUpdate.restriction = UpdateParameter.UPDATE_RESTRICTION_HOMESP;
+ config.policy.policyUpdate.serverUri = "policy.update.com";
+ config.policy.policyUpdate.username = "updateUser";
+ config.policy.policyUpdate.base64EncodedPassword = "updatePass";
+ config.policy.policyUpdate.trustRootCertUrl = "update.cert.com";
+ config.policy.policyUpdate.trustRootCertSha256Fingerprint = new byte[32];
+ Arrays.fill(config.policy.policyUpdate.trustRootCertSha256Fingerprint, (byte) 0x1f);
+
return config;
}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
new file mode 100644
index 000000000000..c371c497f26d
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.net.wifi.hotspot2.pps;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+
+import org.junit.Test;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.pps.Policy}.
+ */
+@SmallTest
+public class PolicyTest {
+ private static final int MAX_NUMBER_OF_EXCLUDED_SSIDS = 128;
+ private static final int MAX_SSID_BYTES = 32;
+ private static final int MAX_PORT_STRING_BYTES = 64;
+
+ /**
+ * Helper function for creating a {@link Policy} for testing.
+ *
+ * @return {@link Policy}
+ */
+ private static Policy createPolicy() {
+ Policy policy = new Policy();
+ policy.minHomeDownlinkBandwidth = 123;
+ policy.minHomeUplinkBandwidth = 345;
+ policy.minRoamingDownlinkBandwidth = 567;
+ policy.minRoamingUplinkBandwidth = 789;
+ policy.excludedSsidList = new String[] {"ssid1", "ssid2"};
+ policy.requiredProtoPortMap = new HashMap<>();
+ policy.requiredProtoPortMap.put(12, "23,342,123");
+ policy.requiredProtoPortMap.put(23, "789,372,1235");
+ policy.maximumBssLoadValue = 12;
+
+ policy.preferredRoamingPartnerList = new ArrayList<>();
+ Policy.RoamingPartner partner1 = new Policy.RoamingPartner();
+ partner1.fqdn = "partner1.com";
+ partner1.fqdnExactMatch = true;
+ partner1.priority = 12;
+ partner1.countries = "us,jp";
+ Policy.RoamingPartner partner2 = new Policy.RoamingPartner();
+ partner2.fqdn = "partner2.com";
+ partner2.fqdnExactMatch = false;
+ partner2.priority = 42;
+ partner2.countries = "ca,fr";
+ policy.preferredRoamingPartnerList.add(partner1);
+ policy.preferredRoamingPartnerList.add(partner2);
+
+ policy.policyUpdate = new UpdateParameter();
+ policy.policyUpdate.updateIntervalInMinutes = 1712;
+ policy.policyUpdate.updateMethod = UpdateParameter.UPDATE_METHOD_OMADM;
+ policy.policyUpdate.restriction = UpdateParameter.UPDATE_RESTRICTION_HOMESP;
+ policy.policyUpdate.serverUri = "policy.update.com";
+ policy.policyUpdate.username = "username";
+ policy.policyUpdate.base64EncodedPassword =
+ Base64.encodeToString("password".getBytes(), Base64.DEFAULT);
+ policy.policyUpdate.trustRootCertUrl = "trust.cert.com";
+ policy.policyUpdate.trustRootCertSha256Fingerprint = new byte[32];
+
+ return policy;
+ }
+
+ /**
+ * Helper function for verifying Policy after parcel write then read.
+ * @param policyToWrite
+ * @throws Exception
+ */
+ private static void verifyParcel(Policy policyToWrite) throws Exception {
+ Parcel parcel = Parcel.obtain();
+ policyToWrite.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
+ Policy policyFromRead = Policy.CREATOR.createFromParcel(parcel);
+ assertTrue(policyFromRead.equals(policyToWrite));
+ }
+
+ /**
+ * Verify parcel read/write for an empty Policy.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithEmptyPolicy() throws Exception {
+ verifyParcel(new Policy());
+ }
+
+ /**
+ * Verify parcel read/write for a Policy with all fields set.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithFullPolicy() throws Exception {
+ verifyParcel(createPolicy());
+ }
+
+ /**
+ * Verify parcel read/write for a Policy without protocol port map.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithoutProtoPortMap() throws Exception {
+ Policy policy = createPolicy();
+ policy.requiredProtoPortMap = null;
+ verifyParcel(policy);
+ }
+
+ /**
+ * Verify parcel read/write for a Policy without preferred roaming partner list.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithoutPreferredRoamingPartnerList() throws Exception {
+ Policy policy = createPolicy();
+ policy.preferredRoamingPartnerList = null;
+ verifyParcel(policy);
+ }
+
+ /**
+ * Verify parcel read/write for a Policy without policy update parameters.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithoutPolicyUpdate() throws Exception {
+ Policy policy = createPolicy();
+ policy.policyUpdate = null;
+ verifyParcel(policy);
+ }
+
+ /**
+ * Verify that policy created using copy constructor with null source should be the same
+ * as the policy created using default constructor.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyCopyConstructionWithNullSource() throws Exception {
+ Policy copyPolicy = new Policy(null);
+ Policy defaultPolicy = new Policy();
+ assertTrue(defaultPolicy.equals(copyPolicy));
+ }
+
+ /**
+ * Verify that policy created using copy constructor with a valid source should be the
+ * same as the source.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyCopyConstructionWithFullPolicy() throws Exception {
+ Policy policy = createPolicy();
+ Policy copyPolicy = new Policy(policy);
+ assertTrue(policy.equals(copyPolicy));
+ }
+
+ /**
+ * Verify that a default policy (with no informatio) is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithDefault() throws Exception {
+ Policy policy = new Policy();
+ assertFalse(policy.validate());
+ }
+
+ /**
+ * Verify that a policy created using {@link #createPolicy} is valid, since all fields are
+ * filled in with valid values.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithFullPolicy() throws Exception {
+ assertTrue(createPolicy().validate());
+ }
+
+ /**
+ * Verify that a policy without policy update parameters is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithoutPolicyUpdate() throws Exception {
+ Policy policy = createPolicy();
+ policy.policyUpdate = null;
+ assertFalse(policy.validate());
+ }
+
+ /**
+ * Verify that a policy with invalid policy update parameters is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithInvalidPolicyUpdate() throws Exception {
+ Policy policy = createPolicy();
+ policy.policyUpdate = new UpdateParameter();
+ assertFalse(policy.validate());
+ }
+
+ /**
+ * Verify that a policy with a preferred roaming partner with FQDN not specified is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithRoamingPartnerWithoutFQDN() throws Exception {
+ Policy policy = createPolicy();
+ Policy.RoamingPartner partner = new Policy.RoamingPartner();
+ partner.fqdnExactMatch = true;
+ partner.priority = 12;
+ partner.countries = "us,jp";
+ policy.preferredRoamingPartnerList.add(partner);
+ assertFalse(policy.validate());
+ }
+
+ /**
+ * Verify that a policy with a preferred roaming partner with countries not specified is
+ * invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithRoamingPartnerWithoutCountries() throws Exception {
+ Policy policy = createPolicy();
+ Policy.RoamingPartner partner = new Policy.RoamingPartner();
+ partner.fqdn = "test.com";
+ partner.fqdnExactMatch = true;
+ partner.priority = 12;
+ policy.preferredRoamingPartnerList.add(partner);
+ assertFalse(policy.validate());
+ }
+
+ /**
+ * Verify that a policy with a proto-port tuple that contains an invalid port string is
+ * invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithInvalidPortStringInProtoPortMap() throws Exception {
+ Policy policy = createPolicy();
+ byte[] rawPortBytes = new byte[MAX_PORT_STRING_BYTES + 1];
+ policy.requiredProtoPortMap.put(324, new String(rawPortBytes, StandardCharsets.UTF_8));
+ assertFalse(policy.validate());
+ }
+
+ /**
+ * Verify that a policy with number of excluded SSIDs exceeded the max is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithSsidExclusionListSizeExceededMax() throws Exception {
+ Policy policy = createPolicy();
+ policy.excludedSsidList = new String[MAX_NUMBER_OF_EXCLUDED_SSIDS + 1];
+ Arrays.fill(policy.excludedSsidList, "ssid");
+ assertFalse(policy.validate());
+ }
+
+ /**
+ * Verify that a policy with an invalid SSID in the excluded SSID list is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithInvalidSsid() throws Exception {
+ Policy policy = createPolicy();
+ byte[] rawSsidBytes = new byte[MAX_SSID_BYTES + 1];
+ Arrays.fill(rawSsidBytes, (byte) 'a');
+ policy.excludedSsidList = new String[] {new String(rawSsidBytes, StandardCharsets.UTF_8)};
+ assertFalse(policy.validate());
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
new file mode 100644
index 000000000000..6bf0db1b4358
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.net.wifi.hotspot2.pps;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+
+import org.junit.Test;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.pps.UpdateParameter}.
+ */
+@SmallTest
+public class UpdateParameterTest {
+ private static final int MAX_URI_BYTES = 1023;
+ private static final int MAX_URL_BYTES = 1023;
+ private static final int MAX_USERNAME_BYTES = 63;
+ private static final int MAX_PASSWORD_BYTES = 255;
+ private static final int CERTIFICATE_SHA256_BYTES = 32;
+
+ /**
+ * Helper function for creating a {@link UpdateParameter} for testing.
+ *
+ * @return {@link UpdateParameter}
+ */
+ private static UpdateParameter createUpdateParameter() {
+ UpdateParameter updateParam = new UpdateParameter();
+ updateParam.updateIntervalInMinutes = 1712;
+ updateParam.updateMethod = UpdateParameter.UPDATE_METHOD_OMADM;
+ updateParam.restriction = UpdateParameter.UPDATE_RESTRICTION_HOMESP;
+ updateParam.serverUri = "server.pdate.com";
+ updateParam.username = "username";
+ updateParam.base64EncodedPassword =
+ Base64.encodeToString("password".getBytes(), Base64.DEFAULT);
+ updateParam.trustRootCertUrl = "trust.cert.com";
+ updateParam.trustRootCertSha256Fingerprint = new byte[32];
+ return updateParam;
+ }
+
+ /**
+ * Helper function for verifying UpdateParameter after parcel write then read.
+ * @param paramToWrite The UpdateParamter to verify
+ * @throws Exception
+ */
+ private static void verifyParcel(UpdateParameter paramToWrite) throws Exception {
+ Parcel parcel = Parcel.obtain();
+ paramToWrite.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
+ UpdateParameter paramFromRead = UpdateParameter.CREATOR.createFromParcel(parcel);
+ assertTrue(paramFromRead.equals(paramToWrite));
+ }
+
+ /**
+ * Verify parcel read/write for an empty UpdateParameter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithEmptyUpdateParameter() throws Exception {
+ verifyParcel(new UpdateParameter());
+ }
+
+ /**
+ * Verify parcel read/write for a UpdateParameter with all fields set.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyParcelWithFullUpdateParameter() throws Exception {
+ verifyParcel(createUpdateParameter());
+ }
+
+ /**
+ * Verify that UpdateParameter created using copy constructor with null source should be the
+ * same as the UpdateParameter created using default constructor.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyCopyConstructionWithNullSource() throws Exception {
+ UpdateParameter copyParam = new UpdateParameter(null);
+ UpdateParameter defaultParam = new UpdateParameter();
+ assertTrue(defaultParam.equals(copyParam));
+ }
+
+ /**
+ * Verify that UpdateParameter created using copy constructor with a valid source should be the
+ * same as the source.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyCopyConstructionWithFullUpdateParameter() throws Exception {
+ UpdateParameter origParam = createUpdateParameter();
+ UpdateParameter copyParam = new UpdateParameter(origParam);
+ assertTrue(origParam.equals(copyParam));
+ }
+
+ /**
+ * Verify that a default UpdateParameter is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithDefault() throws Exception {
+ UpdateParameter updateParam = new UpdateParameter();
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter created using {@link #createUpdateParameter} is valid,
+ * since all fields are filled in with valid values.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithFullPolicy() throws Exception {
+ assertTrue(createUpdateParameter().validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with an unknown update method is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithUnknowMethod() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.updateMethod = "adsfasd";
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with an unknown restriction is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithUnknowRestriction() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.restriction = "adsfasd";
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with an username exceeding maximum size is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithUsernameExceedingMaxSize() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ byte[] rawUsernameBytes = new byte[MAX_USERNAME_BYTES + 1];
+ Arrays.fill(rawUsernameBytes, (byte) 'a');
+ updateParam.username = new String(rawUsernameBytes, StandardCharsets.UTF_8);
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with an empty username is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithEmptyUsername() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.username = null;
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with a password exceeding maximum size is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithPasswordExceedingMaxSize() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ byte[] rawPasswordBytes = new byte[MAX_PASSWORD_BYTES + 1];
+ Arrays.fill(rawPasswordBytes, (byte) 'a');
+ updateParam.base64EncodedPassword = new String(rawPasswordBytes, StandardCharsets.UTF_8);
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with an empty password is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithEmptyPassword() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.base64EncodedPassword = null;
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with a Base64 encoded password that contained invalid padding
+ * is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithPasswordContainedInvalidPadding() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.base64EncodedPassword = updateParam.base64EncodedPassword + "=";
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter without trust root certificate URL is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithoutTrustRootCertUrl() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.trustRootCertUrl = null;
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with invalid trust root certificate URL is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithInvalidTrustRootCertUrl() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ byte[] rawUrlBytes = new byte[MAX_URL_BYTES + 1];
+ Arrays.fill(rawUrlBytes, (byte) 'a');
+ updateParam.trustRootCertUrl = new String(rawUrlBytes, StandardCharsets.UTF_8);
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter without trust root certificate SHA-256 fingerprint is
+ * invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithouttrustRootCertSha256Fingerprint() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.trustRootCertSha256Fingerprint = null;
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with an incorrect size trust root certificate SHA-256
+ * fingerprint is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithInvalidtrustRootCertSha256Fingerprint() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.trustRootCertSha256Fingerprint = new byte[CERTIFICATE_SHA256_BYTES + 1];
+ assertFalse(updateParam.validate());
+
+ updateParam.trustRootCertSha256Fingerprint = new byte[CERTIFICATE_SHA256_BYTES - 1];
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter without server URI is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithoutServerUri() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.serverUri = null;
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with an invalid server URI is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validatePolicyWithInvalidServerUri() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ byte[] rawUriBytes = new byte[MAX_URI_BYTES + 1];
+ Arrays.fill(rawUriBytes, (byte) 'a');
+ updateParam.serverUri = new String(rawUriBytes, StandardCharsets.UTF_8);
+ assertFalse(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with update interval set to "never" will not perform
+ * validation on other parameters, since update is not applicable in this case.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithNoServerCheck() throws Exception {
+ UpdateParameter updateParam = new UpdateParameter();
+ updateParam.updateIntervalInMinutes = UpdateParameter.UPDATE_CHECK_INTERVAL_NEVER;
+ updateParam.username = null;
+ updateParam.base64EncodedPassword = null;
+ updateParam.updateMethod = null;
+ updateParam.restriction = null;
+ updateParam.serverUri = null;
+ updateParam.trustRootCertUrl = null;
+ updateParam.trustRootCertSha256Fingerprint = null;
+ assertTrue(updateParam.validate());
+ }
+
+ /**
+ * Verify that an UpdateParameter with unset update interval is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUpdateParameterWithoutUpdateInterval() throws Exception {
+ UpdateParameter updateParam = createUpdateParameter();
+ updateParam.updateIntervalInMinutes = Long.MIN_VALUE;
+ assertFalse(updateParam.validate());
+ }
+}