Loading wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java +226 −0 Original line number Diff line number Diff line Loading @@ -21,11 +21,14 @@ import android.net.wifi.hotspot2.pps.Credential; import android.net.wifi.hotspot2.pps.HomeSP; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.xml.sax.SAXException; Loading Loading @@ -131,6 +134,14 @@ public final class PPSMOParser { private static final String NODE_FQDN = "FQDN"; private static final String NODE_FRIENDLY_NAME = "FriendlyName"; private static final String NODE_ROAMING_CONSORTIUM_OI = "RoamingConsortiumOI"; private static final String NODE_NETWORK_ID = "NetworkID"; private static final String NODE_SSID = "SSID"; private static final String NODE_HESSID = "HESSID"; private static final String NODE_ICON_URL = "IconURL"; private static final String NODE_HOME_OI_LIST = "HomeOIList"; private static final String NODE_HOME_OI = "HomeOI"; private static final String NODE_HOME_OI_REQUIRED = "HomeOIRequired"; private static final String NODE_OTHER_HOME_PARTNERS = "OtherHomePartners"; /** * Fields under Credential subtree. Loading Loading @@ -558,6 +569,20 @@ public final class PPSMOParser { homeSp.roamingConsortiumOIs = parseRoamingConsortiumOI(getPpsNodeValue(child)); break; case NODE_ICON_URL: homeSp.iconUrl = getPpsNodeValue(child); break; case NODE_NETWORK_ID: homeSp.homeNetworkIds = parseNetworkIds(child); break; case NODE_HOME_OI_LIST: Pair<List<Long>, List<Long>> homeOIs = parseHomeOIList(child); homeSp.matchAllOIs = convertFromLongList(homeOIs.first); homeSp.matchAnyOIs = convertFromLongList(homeOIs.second); break; case NODE_OTHER_HOME_PARTNERS: homeSp.otherHomePartners = parseOtherHomePartners(child); break; default: throw new ParsingException("Unknown node under HomeSP: " + child.getName()); } Loading Loading @@ -586,6 +611,192 @@ public final class PPSMOParser { return oiArray; } /** * Parse configurations under PerProviderSubscription/HomeSP/NetworkID subtree. * * @param node PPSNode representing the root of the PerProviderSubscription/HomeSP/NetworkID * subtree * @return HashMap<String, Long> representing list of <SSID, HESSID> pair. * @throws ParsingException */ static private Map<String, Long> parseNetworkIds(PPSNode node) throws ParsingException { if (node.isLeaf()) { throw new ParsingException("Leaf node not expected for NetworkID"); } Map<String, Long> networkIds = new HashMap<>(); for (PPSNode child : node.getChildren()) { Pair<String, Long> networkId = parseNetworkIdInstance(child); networkIds.put(networkId.first, networkId.second); } return networkIds; } /** * Parse configurations under PerProviderSubscription/HomeSP/NetworkID/<X+> subtree. * The instance name (<X+>) is irrelevant and must be unique for each instance, which * is verified when the PPS tree is constructed {@link #buildPpsNode}. * * @param node PPSNode representing the root of the * PerProviderSubscription/HomeSP/NetworkID/<X+> subtree * @return Pair<String, Long> representing <SSID, HESSID> pair. * @throws ParsingException */ static private Pair<String, Long> parseNetworkIdInstance(PPSNode node) throws ParsingException { if (node.isLeaf()) { throw new ParsingException("Leaf node not expected for NetworkID instance"); } String ssid = null; Long hessid = null; for (PPSNode child : node.getChildren()) { switch (child.getName()) { case NODE_SSID: ssid = getPpsNodeValue(child); break; case NODE_HESSID: try { hessid = Long.parseLong(getPpsNodeValue(child), 16); } catch (NumberFormatException e) { throw new ParsingException("Invalid HESSID: " + getPpsNodeValue(child)); } break; default: throw new ParsingException("Unknown node under NetworkID instance: " + child.getName()); } } if (ssid == null) throw new ParsingException("NetworkID instance missing SSID"); return new Pair<String, Long>(ssid, hessid); } /** * Parse configurations under PerProviderSubscription/HomeSP/HomeOIList subtree. * * @param node PPSNode representing the root of the PerProviderSubscription/HomeSP/HomeOIList * subtree * @return Pair<List<Long>, List<Long>> containing both MatchAllOIs and MatchAnyOIs list. * @throws ParsingException */ private static Pair<List<Long>, List<Long>> parseHomeOIList(PPSNode node) throws ParsingException { if (node.isLeaf()) { throw new ParsingException("Leaf node not expected for HomeOIList"); } List<Long> matchAllOIs = new ArrayList<Long>(); List<Long> matchAnyOIs = new ArrayList<Long>(); for (PPSNode child : node.getChildren()) { Pair<Long, Boolean> homeOI = parseHomeOIInstance(child); if (homeOI.second.booleanValue()) { matchAllOIs.add(homeOI.first); } else { matchAnyOIs.add(homeOI.first); } } return new Pair<List<Long>, List<Long>>(matchAllOIs, matchAnyOIs); } /** * Parse configurations under PerProviderSubscription/HomeSP/HomeOIList/<X+> subtree. * The instance name (<X+>) is irrelevant and must be unique for each instance, which * is verified when the PPS tree is constructed {@link #buildPpsNode}. * * @param node PPSNode representing the root of the * PerProviderSubscription/HomeSP/HomeOIList/<X+> subtree * @return Pair<Long, Boolean> containing a HomeOI and a HomeOIRequired flag * @throws ParsingException */ private static Pair<Long, Boolean> parseHomeOIInstance(PPSNode node) throws ParsingException { if (node.isLeaf()) { throw new ParsingException("Leaf node not expected for HomeOI instance"); } Long oi = null; Boolean required = null; for (PPSNode child : node.getChildren()) { switch (child.getName()) { case NODE_HOME_OI: try { oi = Long.valueOf(getPpsNodeValue(child), 16); } catch (NumberFormatException e) { throw new ParsingException("Invalid HomeOI: " + getPpsNodeValue(child)); } break; case NODE_HOME_OI_REQUIRED: required = Boolean.valueOf(getPpsNodeValue(child)); break; default: throw new ParsingException("Unknown node under NetworkID instance: " + child.getName()); } } if (oi == null) { throw new ParsingException("HomeOI instance missing OI field"); } if (required == null) { throw new ParsingException("HomeOI instance missing required field"); } return new Pair<Long, Boolean>(oi, required); } /** * Parse configurations under PerProviderSubscription/HomeSP/OtherHomePartners subtree. * This contains a list of FQDN (Fully Qualified Domain Name) that are considered * home partners. * * @param node PPSNode representing the root of the * PerProviderSubscription/HomeSP/OtherHomePartners subtree * @return String[] list of partner's FQDN * @throws ParsingException */ private static String[] parseOtherHomePartners(PPSNode node) throws ParsingException { if (node.isLeaf()) { throw new ParsingException("Leaf node not expected for OtherHomePartners"); } List<String> otherHomePartners = new ArrayList<String>(); for (PPSNode child : node.getChildren()) { String fqdn = parseOtherHomePartnerInstance(child); otherHomePartners.add(fqdn); } return otherHomePartners.toArray(new String[otherHomePartners.size()]); } /** * Parse configurations under PerProviderSubscription/HomeSP/OtherHomePartners/<X+> subtree. * The instance name (<X+>) is irrelevant and must be unique for each instance, which * is verified when the PPS tree is constructed {@link #buildPpsNode}. * * @param node PPSNode representing the root of the * PerProviderSubscription/HomeSP/OtherHomePartners/<X+> subtree * @return String FQDN of the partner * @throws ParsingException */ private static String parseOtherHomePartnerInstance(PPSNode node) throws ParsingException { if (node.isLeaf()) { throw new ParsingException("Leaf node not expected for OtherHomePartner instance"); } String fqdn = null; for (PPSNode child : node.getChildren()) { switch (child.getName()) { case NODE_FQDN: fqdn = getPpsNodeValue(child); break; default: throw new ParsingException( "Unknown node under OtherHomePartner instance: " + child.getName()); } } if (fqdn == null) { throw new ParsingException("OtherHomePartner instance missing FQDN field"); } return fqdn; } /** * Parse configurations under PerProviderSubscription/Credential subtree. * Loading Loading @@ -783,4 +994,19 @@ public final class PPSMOParser { throw new ParsingException("Invalid integer value: " + value); } } /** * Convert a List<Long> to a primitive long array long[]. * * @param list List to be converted * @return long[] */ private static long[] convertFromLongList(List<Long> list) { Long[] objectArray = list.toArray(new Long[list.size()]); long[] primitiveArray = new long[objectArray.length]; for (int i = 0; i < objectArray.length; i++) { primitiveArray[i] = objectArray[i].longValue(); } return primitiveArray; } } wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java +160 −12 Original line number Diff line number Diff line Loading @@ -21,7 +21,11 @@ import android.os.Parcel; import android.text.TextUtils; import android.util.Log; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Class representing HomeSP subtree in PerProviderSubscription (PPS) Loading @@ -30,13 +34,21 @@ import java.util.Arrays; * 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 we only support the nodes that are used by Hotspot 2.0 Release 1. * * @hide */ public final class HomeSP implements Parcelable { private static final String TAG = "HomeSP"; /** * Maximum number of bytes allowed for a SSID. */ private static final int MAX_SSID_BYTES = 32; /** * Integer value used for indicating null value in the Parcel. */ private static final int NULL_VALUE = -1; /** * FQDN (Fully Qualified Domain Name) of this home service provider. */ Loading @@ -47,6 +59,55 @@ public final class HomeSP implements Parcelable { */ public String friendlyName = null; /** * Icon URL of this home service provider. */ public String iconUrl = null; /** * <SSID, HESSID> duple of the networks that are consider home networks. * * According to the Section 9.1.2 of the Hotspot 2.0 Release 2 Technical Specification, * all nodes in the PSS MO are encoded using UTF-8 unless stated otherwise. Thus, the SSID * string is assumed to be encoded using UTF-8. */ public Map<String, Long> homeNetworkIds = null; /** * Used for determining if this provider is a member of a given Hotspot provider. * Every Organization Identifiers (OIs) in this list are required to match an OI in the * the Roaming Consortium advertised by a Hotspot, in order to consider this provider * as a member of that Hotspot provider (e.g. successful authentication with such Hotspot * is possible). * * Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object * (MO) tree for more detail. */ public long[] matchAllOIs = null; /** * Used for determining if this provider is a member of a given Hotspot provider. * Matching of any Organization Identifiers (OIs) in this list with an OI in the * Roaming Consortium advertised by a Hotspot, will consider this provider as a member * of that Hotspot provider (e.g. successful authentication with such Hotspot * is possible). * * {@link #matchAllOIs} will have precedence over this one, meaning this list will * only be used for matching if {@link #matchAllOIs} is null or empty. * * Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object * (MO) tree for more detail. */ public long[] matchAnyOIs = null; /** * List of FQDN (Fully Qualified Domain Name) of partner providers. * These providers should also be regarded as home Hotspot operators. * This relationship is most likely achieved via a commercial agreement or * operator merges between the providers. */ public String[] otherHomePartners = null; /** * List of Organization Identifiers (OIs) identifying a roaming consortium of * which this provider is a member. Loading @@ -64,15 +125,30 @@ public final class HomeSP implements Parcelable { * @param source The source to copy from */ public HomeSP(HomeSP source) { if (source != null) { if (source == null) { return; } fqdn = source.fqdn; friendlyName = source.friendlyName; iconUrl = source.iconUrl; if (source.homeNetworkIds != null) { homeNetworkIds = Collections.unmodifiableMap(source.homeNetworkIds); } if (source.matchAllOIs != null) { matchAllOIs = Arrays.copyOf(source.matchAllOIs, source.matchAllOIs.length); } if (source.matchAnyOIs != null) { matchAnyOIs = Arrays.copyOf(source.matchAnyOIs, source.matchAnyOIs.length); } if (source.otherHomePartners != null) { otherHomePartners = Arrays.copyOf(source.otherHomePartners, source.otherHomePartners.length); } if (source.roamingConsortiumOIs != null) { roamingConsortiumOIs = Arrays.copyOf(source.roamingConsortiumOIs, source.roamingConsortiumOIs.length); } } } @Override public int describeContents() { Loading @@ -83,6 +159,11 @@ public final class HomeSP implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(fqdn); dest.writeString(friendlyName); dest.writeString(iconUrl); writeHomeNetworkIds(dest, homeNetworkIds); dest.writeLongArray(matchAllOIs); dest.writeLongArray(matchAnyOIs); dest.writeStringArray(otherHomePartners); dest.writeLongArray(roamingConsortiumOIs); } Loading @@ -96,9 +177,15 @@ public final class HomeSP implements Parcelable { } HomeSP that = (HomeSP) thatObject; return TextUtils.equals(fqdn, that.fqdn) && TextUtils.equals(friendlyName, that.friendlyName) && Arrays.equals(roamingConsortiumOIs, that.roamingConsortiumOIs); return TextUtils.equals(fqdn, that.fqdn) && TextUtils.equals(friendlyName, that.friendlyName) && TextUtils.equals(iconUrl, that.iconUrl) && (homeNetworkIds == null ? that.homeNetworkIds == null : homeNetworkIds.equals(that.homeNetworkIds)) && Arrays.equals(matchAllOIs, that.matchAllOIs) && Arrays.equals(matchAnyOIs, that.matchAnyOIs) && Arrays.equals(otherHomePartners, that.otherHomePartners) && Arrays.equals(roamingConsortiumOIs, that.roamingConsortiumOIs); } /** Loading @@ -115,6 +202,16 @@ public final class HomeSP implements Parcelable { Log.d(TAG, "Missing friendly name"); return false; } // Verify SSIDs specified in the NetworkID if (homeNetworkIds != null) { for (Map.Entry<String, Long> entry : homeNetworkIds.entrySet()) { if (entry.getKey() == null || entry.getKey().getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) { Log.d(TAG, "Invalid SSID in HomeNetworkIDs"); return false; } } } return true; } Loading @@ -125,6 +222,11 @@ public final class HomeSP implements Parcelable { HomeSP homeSp = new HomeSP(); homeSp.fqdn = in.readString(); homeSp.friendlyName = in.readString(); homeSp.iconUrl = in.readString(); homeSp.homeNetworkIds = readHomeNetworkIds(in); homeSp.matchAllOIs = in.createLongArray(); homeSp.matchAnyOIs = in.createLongArray(); homeSp.otherHomePartners = in.createStringArray(); homeSp.roamingConsortiumOIs = in.createLongArray(); return homeSp; } Loading @@ -133,5 +235,51 @@ public final class HomeSP implements Parcelable { public HomeSP[] newArray(int size) { return new HomeSP[size]; } /** * Helper function for reading a Home Network IDs map from a Parcel. * * @param in The Parcel to read from * @return Map of home network IDs */ private Map<String, Long> readHomeNetworkIds(Parcel in) { int size = in.readInt(); if (size == NULL_VALUE) { return null; } Map<String, Long> networkIds = new HashMap<>(size); for (int i = 0; i < size; i++) { String key = in.readString(); Long value = null; long readValue = in.readLong(); if (readValue != NULL_VALUE) { value = Long.valueOf(readValue); } networkIds.put(key, value); } return networkIds; } }; /** * Helper function for writing Home Network IDs map to a Parcel. * * @param dest The Parcel to write to * @param networkIds The map of home network IDs */ private static void writeHomeNetworkIds(Parcel dest, Map<String, Long> networkIds) { if (networkIds == null) { dest.writeInt(NULL_VALUE); return; } dest.writeInt(networkIds.size()); for (Map.Entry<String, Long> entry : networkIds.entrySet()) { dest.writeString(entry.getKey()); if (entry.getValue() == null) { dest.writeLong(NULL_VALUE); } else { dest.writeLong(entry.getValue()); } } } } wifi/tests/assets/pps/PerProviderSubscription.xml +60 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,66 @@ <NodeName>RoamingConsortiumOI</NodeName> <Value>112233,445566</Value> </Node> <Node> <NodeName>IconURL</NodeName> <Value>icon.test.com</Value> </Node> <Node> <NodeName>NetworkID</NodeName> <Node> <NodeName>n001</NodeName> <Node> <NodeName>SSID</NodeName> <Value>TestSSID</Value> </Node> <Node> <NodeName>HESSID</NodeName> <Value>12345678</Value> </Node> </Node> <Node> <NodeName>n002</NodeName> <Node> <NodeName>SSID</NodeName> <Value>NullHESSID</Value> </Node> </Node> </Node> <Node> <NodeName>HomeOIList</NodeName> <Node> <NodeName>h001</NodeName> <Node> <NodeName>HomeOI</NodeName> <Value>11223344</Value> </Node> <Node> <NodeName>HomeOIRequired</NodeName> <Value>true</Value> </Node> </Node> <Node> <NodeName>h002</NodeName> <Node> <NodeName>HomeOI</NodeName> <Value>55667788</Value> </Node> <Node> <NodeName>HomeOIRequired</NodeName> <Value>false</Value> </Node> </Node> </Node> <Node> <NodeName>OtherHomePartners</NodeName> <Node> <NodeName>o001</NodeName> <Node> <NodeName>FQDN</NodeName> <Value>other.fqdn.com</Value> </Node> </Node> </Node> </Node> <Node> <NodeName>Credential</NodeName> Loading wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java +8 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Arrays; import java.util.HashMap; /** * Unit tests for {@link android.net.wifi.hotspot2.omadm.PPSMOParser}. Loading Loading @@ -85,6 +86,13 @@ public class PPSMOParserTest { config.homeSp.friendlyName = "Century House"; config.homeSp.fqdn = "mi6.co.uk"; config.homeSp.roamingConsortiumOIs = new long[] {0x112233L, 0x445566L}; config.homeSp.iconUrl = "icon.test.com"; config.homeSp.homeNetworkIds = new HashMap<>(); config.homeSp.homeNetworkIds.put("TestSSID", 0x12345678L); config.homeSp.homeNetworkIds.put("NullHESSID", null); config.homeSp.matchAllOIs = new long[] {0x11223344}; config.homeSp.matchAnyOIs = new long[] {0x55667788}; config.homeSp.otherHomePartners = new String[] {"other.fqdn.com"}; // Credential configuration. config.credential = new Credential(); Loading wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java +110 −8 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java +226 −0 Original line number Diff line number Diff line Loading @@ -21,11 +21,14 @@ import android.net.wifi.hotspot2.pps.Credential; import android.net.wifi.hotspot2.pps.HomeSP; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.xml.sax.SAXException; Loading Loading @@ -131,6 +134,14 @@ public final class PPSMOParser { private static final String NODE_FQDN = "FQDN"; private static final String NODE_FRIENDLY_NAME = "FriendlyName"; private static final String NODE_ROAMING_CONSORTIUM_OI = "RoamingConsortiumOI"; private static final String NODE_NETWORK_ID = "NetworkID"; private static final String NODE_SSID = "SSID"; private static final String NODE_HESSID = "HESSID"; private static final String NODE_ICON_URL = "IconURL"; private static final String NODE_HOME_OI_LIST = "HomeOIList"; private static final String NODE_HOME_OI = "HomeOI"; private static final String NODE_HOME_OI_REQUIRED = "HomeOIRequired"; private static final String NODE_OTHER_HOME_PARTNERS = "OtherHomePartners"; /** * Fields under Credential subtree. Loading Loading @@ -558,6 +569,20 @@ public final class PPSMOParser { homeSp.roamingConsortiumOIs = parseRoamingConsortiumOI(getPpsNodeValue(child)); break; case NODE_ICON_URL: homeSp.iconUrl = getPpsNodeValue(child); break; case NODE_NETWORK_ID: homeSp.homeNetworkIds = parseNetworkIds(child); break; case NODE_HOME_OI_LIST: Pair<List<Long>, List<Long>> homeOIs = parseHomeOIList(child); homeSp.matchAllOIs = convertFromLongList(homeOIs.first); homeSp.matchAnyOIs = convertFromLongList(homeOIs.second); break; case NODE_OTHER_HOME_PARTNERS: homeSp.otherHomePartners = parseOtherHomePartners(child); break; default: throw new ParsingException("Unknown node under HomeSP: " + child.getName()); } Loading Loading @@ -586,6 +611,192 @@ public final class PPSMOParser { return oiArray; } /** * Parse configurations under PerProviderSubscription/HomeSP/NetworkID subtree. * * @param node PPSNode representing the root of the PerProviderSubscription/HomeSP/NetworkID * subtree * @return HashMap<String, Long> representing list of <SSID, HESSID> pair. * @throws ParsingException */ static private Map<String, Long> parseNetworkIds(PPSNode node) throws ParsingException { if (node.isLeaf()) { throw new ParsingException("Leaf node not expected for NetworkID"); } Map<String, Long> networkIds = new HashMap<>(); for (PPSNode child : node.getChildren()) { Pair<String, Long> networkId = parseNetworkIdInstance(child); networkIds.put(networkId.first, networkId.second); } return networkIds; } /** * Parse configurations under PerProviderSubscription/HomeSP/NetworkID/<X+> subtree. * The instance name (<X+>) is irrelevant and must be unique for each instance, which * is verified when the PPS tree is constructed {@link #buildPpsNode}. * * @param node PPSNode representing the root of the * PerProviderSubscription/HomeSP/NetworkID/<X+> subtree * @return Pair<String, Long> representing <SSID, HESSID> pair. * @throws ParsingException */ static private Pair<String, Long> parseNetworkIdInstance(PPSNode node) throws ParsingException { if (node.isLeaf()) { throw new ParsingException("Leaf node not expected for NetworkID instance"); } String ssid = null; Long hessid = null; for (PPSNode child : node.getChildren()) { switch (child.getName()) { case NODE_SSID: ssid = getPpsNodeValue(child); break; case NODE_HESSID: try { hessid = Long.parseLong(getPpsNodeValue(child), 16); } catch (NumberFormatException e) { throw new ParsingException("Invalid HESSID: " + getPpsNodeValue(child)); } break; default: throw new ParsingException("Unknown node under NetworkID instance: " + child.getName()); } } if (ssid == null) throw new ParsingException("NetworkID instance missing SSID"); return new Pair<String, Long>(ssid, hessid); } /** * Parse configurations under PerProviderSubscription/HomeSP/HomeOIList subtree. * * @param node PPSNode representing the root of the PerProviderSubscription/HomeSP/HomeOIList * subtree * @return Pair<List<Long>, List<Long>> containing both MatchAllOIs and MatchAnyOIs list. * @throws ParsingException */ private static Pair<List<Long>, List<Long>> parseHomeOIList(PPSNode node) throws ParsingException { if (node.isLeaf()) { throw new ParsingException("Leaf node not expected for HomeOIList"); } List<Long> matchAllOIs = new ArrayList<Long>(); List<Long> matchAnyOIs = new ArrayList<Long>(); for (PPSNode child : node.getChildren()) { Pair<Long, Boolean> homeOI = parseHomeOIInstance(child); if (homeOI.second.booleanValue()) { matchAllOIs.add(homeOI.first); } else { matchAnyOIs.add(homeOI.first); } } return new Pair<List<Long>, List<Long>>(matchAllOIs, matchAnyOIs); } /** * Parse configurations under PerProviderSubscription/HomeSP/HomeOIList/<X+> subtree. * The instance name (<X+>) is irrelevant and must be unique for each instance, which * is verified when the PPS tree is constructed {@link #buildPpsNode}. * * @param node PPSNode representing the root of the * PerProviderSubscription/HomeSP/HomeOIList/<X+> subtree * @return Pair<Long, Boolean> containing a HomeOI and a HomeOIRequired flag * @throws ParsingException */ private static Pair<Long, Boolean> parseHomeOIInstance(PPSNode node) throws ParsingException { if (node.isLeaf()) { throw new ParsingException("Leaf node not expected for HomeOI instance"); } Long oi = null; Boolean required = null; for (PPSNode child : node.getChildren()) { switch (child.getName()) { case NODE_HOME_OI: try { oi = Long.valueOf(getPpsNodeValue(child), 16); } catch (NumberFormatException e) { throw new ParsingException("Invalid HomeOI: " + getPpsNodeValue(child)); } break; case NODE_HOME_OI_REQUIRED: required = Boolean.valueOf(getPpsNodeValue(child)); break; default: throw new ParsingException("Unknown node under NetworkID instance: " + child.getName()); } } if (oi == null) { throw new ParsingException("HomeOI instance missing OI field"); } if (required == null) { throw new ParsingException("HomeOI instance missing required field"); } return new Pair<Long, Boolean>(oi, required); } /** * Parse configurations under PerProviderSubscription/HomeSP/OtherHomePartners subtree. * This contains a list of FQDN (Fully Qualified Domain Name) that are considered * home partners. * * @param node PPSNode representing the root of the * PerProviderSubscription/HomeSP/OtherHomePartners subtree * @return String[] list of partner's FQDN * @throws ParsingException */ private static String[] parseOtherHomePartners(PPSNode node) throws ParsingException { if (node.isLeaf()) { throw new ParsingException("Leaf node not expected for OtherHomePartners"); } List<String> otherHomePartners = new ArrayList<String>(); for (PPSNode child : node.getChildren()) { String fqdn = parseOtherHomePartnerInstance(child); otherHomePartners.add(fqdn); } return otherHomePartners.toArray(new String[otherHomePartners.size()]); } /** * Parse configurations under PerProviderSubscription/HomeSP/OtherHomePartners/<X+> subtree. * The instance name (<X+>) is irrelevant and must be unique for each instance, which * is verified when the PPS tree is constructed {@link #buildPpsNode}. * * @param node PPSNode representing the root of the * PerProviderSubscription/HomeSP/OtherHomePartners/<X+> subtree * @return String FQDN of the partner * @throws ParsingException */ private static String parseOtherHomePartnerInstance(PPSNode node) throws ParsingException { if (node.isLeaf()) { throw new ParsingException("Leaf node not expected for OtherHomePartner instance"); } String fqdn = null; for (PPSNode child : node.getChildren()) { switch (child.getName()) { case NODE_FQDN: fqdn = getPpsNodeValue(child); break; default: throw new ParsingException( "Unknown node under OtherHomePartner instance: " + child.getName()); } } if (fqdn == null) { throw new ParsingException("OtherHomePartner instance missing FQDN field"); } return fqdn; } /** * Parse configurations under PerProviderSubscription/Credential subtree. * Loading Loading @@ -783,4 +994,19 @@ public final class PPSMOParser { throw new ParsingException("Invalid integer value: " + value); } } /** * Convert a List<Long> to a primitive long array long[]. * * @param list List to be converted * @return long[] */ private static long[] convertFromLongList(List<Long> list) { Long[] objectArray = list.toArray(new Long[list.size()]); long[] primitiveArray = new long[objectArray.length]; for (int i = 0; i < objectArray.length; i++) { primitiveArray[i] = objectArray[i].longValue(); } return primitiveArray; } }
wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java +160 −12 Original line number Diff line number Diff line Loading @@ -21,7 +21,11 @@ import android.os.Parcel; import android.text.TextUtils; import android.util.Log; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Class representing HomeSP subtree in PerProviderSubscription (PPS) Loading @@ -30,13 +34,21 @@ import java.util.Arrays; * 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 we only support the nodes that are used by Hotspot 2.0 Release 1. * * @hide */ public final class HomeSP implements Parcelable { private static final String TAG = "HomeSP"; /** * Maximum number of bytes allowed for a SSID. */ private static final int MAX_SSID_BYTES = 32; /** * Integer value used for indicating null value in the Parcel. */ private static final int NULL_VALUE = -1; /** * FQDN (Fully Qualified Domain Name) of this home service provider. */ Loading @@ -47,6 +59,55 @@ public final class HomeSP implements Parcelable { */ public String friendlyName = null; /** * Icon URL of this home service provider. */ public String iconUrl = null; /** * <SSID, HESSID> duple of the networks that are consider home networks. * * According to the Section 9.1.2 of the Hotspot 2.0 Release 2 Technical Specification, * all nodes in the PSS MO are encoded using UTF-8 unless stated otherwise. Thus, the SSID * string is assumed to be encoded using UTF-8. */ public Map<String, Long> homeNetworkIds = null; /** * Used for determining if this provider is a member of a given Hotspot provider. * Every Organization Identifiers (OIs) in this list are required to match an OI in the * the Roaming Consortium advertised by a Hotspot, in order to consider this provider * as a member of that Hotspot provider (e.g. successful authentication with such Hotspot * is possible). * * Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object * (MO) tree for more detail. */ public long[] matchAllOIs = null; /** * Used for determining if this provider is a member of a given Hotspot provider. * Matching of any Organization Identifiers (OIs) in this list with an OI in the * Roaming Consortium advertised by a Hotspot, will consider this provider as a member * of that Hotspot provider (e.g. successful authentication with such Hotspot * is possible). * * {@link #matchAllOIs} will have precedence over this one, meaning this list will * only be used for matching if {@link #matchAllOIs} is null or empty. * * Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object * (MO) tree for more detail. */ public long[] matchAnyOIs = null; /** * List of FQDN (Fully Qualified Domain Name) of partner providers. * These providers should also be regarded as home Hotspot operators. * This relationship is most likely achieved via a commercial agreement or * operator merges between the providers. */ public String[] otherHomePartners = null; /** * List of Organization Identifiers (OIs) identifying a roaming consortium of * which this provider is a member. Loading @@ -64,15 +125,30 @@ public final class HomeSP implements Parcelable { * @param source The source to copy from */ public HomeSP(HomeSP source) { if (source != null) { if (source == null) { return; } fqdn = source.fqdn; friendlyName = source.friendlyName; iconUrl = source.iconUrl; if (source.homeNetworkIds != null) { homeNetworkIds = Collections.unmodifiableMap(source.homeNetworkIds); } if (source.matchAllOIs != null) { matchAllOIs = Arrays.copyOf(source.matchAllOIs, source.matchAllOIs.length); } if (source.matchAnyOIs != null) { matchAnyOIs = Arrays.copyOf(source.matchAnyOIs, source.matchAnyOIs.length); } if (source.otherHomePartners != null) { otherHomePartners = Arrays.copyOf(source.otherHomePartners, source.otherHomePartners.length); } if (source.roamingConsortiumOIs != null) { roamingConsortiumOIs = Arrays.copyOf(source.roamingConsortiumOIs, source.roamingConsortiumOIs.length); } } } @Override public int describeContents() { Loading @@ -83,6 +159,11 @@ public final class HomeSP implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(fqdn); dest.writeString(friendlyName); dest.writeString(iconUrl); writeHomeNetworkIds(dest, homeNetworkIds); dest.writeLongArray(matchAllOIs); dest.writeLongArray(matchAnyOIs); dest.writeStringArray(otherHomePartners); dest.writeLongArray(roamingConsortiumOIs); } Loading @@ -96,9 +177,15 @@ public final class HomeSP implements Parcelable { } HomeSP that = (HomeSP) thatObject; return TextUtils.equals(fqdn, that.fqdn) && TextUtils.equals(friendlyName, that.friendlyName) && Arrays.equals(roamingConsortiumOIs, that.roamingConsortiumOIs); return TextUtils.equals(fqdn, that.fqdn) && TextUtils.equals(friendlyName, that.friendlyName) && TextUtils.equals(iconUrl, that.iconUrl) && (homeNetworkIds == null ? that.homeNetworkIds == null : homeNetworkIds.equals(that.homeNetworkIds)) && Arrays.equals(matchAllOIs, that.matchAllOIs) && Arrays.equals(matchAnyOIs, that.matchAnyOIs) && Arrays.equals(otherHomePartners, that.otherHomePartners) && Arrays.equals(roamingConsortiumOIs, that.roamingConsortiumOIs); } /** Loading @@ -115,6 +202,16 @@ public final class HomeSP implements Parcelable { Log.d(TAG, "Missing friendly name"); return false; } // Verify SSIDs specified in the NetworkID if (homeNetworkIds != null) { for (Map.Entry<String, Long> entry : homeNetworkIds.entrySet()) { if (entry.getKey() == null || entry.getKey().getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) { Log.d(TAG, "Invalid SSID in HomeNetworkIDs"); return false; } } } return true; } Loading @@ -125,6 +222,11 @@ public final class HomeSP implements Parcelable { HomeSP homeSp = new HomeSP(); homeSp.fqdn = in.readString(); homeSp.friendlyName = in.readString(); homeSp.iconUrl = in.readString(); homeSp.homeNetworkIds = readHomeNetworkIds(in); homeSp.matchAllOIs = in.createLongArray(); homeSp.matchAnyOIs = in.createLongArray(); homeSp.otherHomePartners = in.createStringArray(); homeSp.roamingConsortiumOIs = in.createLongArray(); return homeSp; } Loading @@ -133,5 +235,51 @@ public final class HomeSP implements Parcelable { public HomeSP[] newArray(int size) { return new HomeSP[size]; } /** * Helper function for reading a Home Network IDs map from a Parcel. * * @param in The Parcel to read from * @return Map of home network IDs */ private Map<String, Long> readHomeNetworkIds(Parcel in) { int size = in.readInt(); if (size == NULL_VALUE) { return null; } Map<String, Long> networkIds = new HashMap<>(size); for (int i = 0; i < size; i++) { String key = in.readString(); Long value = null; long readValue = in.readLong(); if (readValue != NULL_VALUE) { value = Long.valueOf(readValue); } networkIds.put(key, value); } return networkIds; } }; /** * Helper function for writing Home Network IDs map to a Parcel. * * @param dest The Parcel to write to * @param networkIds The map of home network IDs */ private static void writeHomeNetworkIds(Parcel dest, Map<String, Long> networkIds) { if (networkIds == null) { dest.writeInt(NULL_VALUE); return; } dest.writeInt(networkIds.size()); for (Map.Entry<String, Long> entry : networkIds.entrySet()) { dest.writeString(entry.getKey()); if (entry.getValue() == null) { dest.writeLong(NULL_VALUE); } else { dest.writeLong(entry.getValue()); } } } }
wifi/tests/assets/pps/PerProviderSubscription.xml +60 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,66 @@ <NodeName>RoamingConsortiumOI</NodeName> <Value>112233,445566</Value> </Node> <Node> <NodeName>IconURL</NodeName> <Value>icon.test.com</Value> </Node> <Node> <NodeName>NetworkID</NodeName> <Node> <NodeName>n001</NodeName> <Node> <NodeName>SSID</NodeName> <Value>TestSSID</Value> </Node> <Node> <NodeName>HESSID</NodeName> <Value>12345678</Value> </Node> </Node> <Node> <NodeName>n002</NodeName> <Node> <NodeName>SSID</NodeName> <Value>NullHESSID</Value> </Node> </Node> </Node> <Node> <NodeName>HomeOIList</NodeName> <Node> <NodeName>h001</NodeName> <Node> <NodeName>HomeOI</NodeName> <Value>11223344</Value> </Node> <Node> <NodeName>HomeOIRequired</NodeName> <Value>true</Value> </Node> </Node> <Node> <NodeName>h002</NodeName> <Node> <NodeName>HomeOI</NodeName> <Value>55667788</Value> </Node> <Node> <NodeName>HomeOIRequired</NodeName> <Value>false</Value> </Node> </Node> </Node> <Node> <NodeName>OtherHomePartners</NodeName> <Node> <NodeName>o001</NodeName> <Node> <NodeName>FQDN</NodeName> <Value>other.fqdn.com</Value> </Node> </Node> </Node> </Node> <Node> <NodeName>Credential</NodeName> Loading
wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java +8 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Arrays; import java.util.HashMap; /** * Unit tests for {@link android.net.wifi.hotspot2.omadm.PPSMOParser}. Loading Loading @@ -85,6 +86,13 @@ public class PPSMOParserTest { config.homeSp.friendlyName = "Century House"; config.homeSp.fqdn = "mi6.co.uk"; config.homeSp.roamingConsortiumOIs = new long[] {0x112233L, 0x445566L}; config.homeSp.iconUrl = "icon.test.com"; config.homeSp.homeNetworkIds = new HashMap<>(); config.homeSp.homeNetworkIds.put("TestSSID", 0x12345678L); config.homeSp.homeNetworkIds.put("NullHESSID", null); config.homeSp.matchAllOIs = new long[] {0x11223344}; config.homeSp.matchAnyOIs = new long[] {0x55667788}; config.homeSp.otherHomePartners = new String[] {"other.fqdn.com"}; // Credential configuration. config.credential = new Credential(); Loading
wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java +110 −8 File changed.Preview size limit exceeded, changes collapsed. Show changes