Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 28c7afe5 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "hotspot2: add support for complete HomeSP subtree"

parents 36654ffe e6e7baa6
Loading
Loading
Loading
Loading
+226 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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.
@@ -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());
            }
@@ -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.
     *
@@ -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;
    }
}
+160 −12
Original line number Diff line number Diff line
@@ -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)
@@ -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.
     */
@@ -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.
@@ -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() {
@@ -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);
    }

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

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

@@ -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;
            }
@@ -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());
            }
        }
    }
}
+60 −0
Original line number Diff line number Diff line
@@ -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>
+8 −0
Original line number Diff line number Diff line
@@ -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}.
@@ -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();
+110 −8

File changed.

Preview size limit exceeded, changes collapsed.