diff options
| author | 2017-01-17 11:41:27 -0800 | |
|---|---|---|
| committer | 2017-01-25 10:14:37 -0800 | |
| commit | 2d7af45e939a49f3b75e275a12824c9be3d2a064 (patch) | |
| tree | 947a44b9a56b07a33951bd1c56b16f00d2f85a44 | |
| parent | 815fb5479868ec7160bdd293766e827c576467e0 (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
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()); + } +} |