1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi.hotspot2.anqp;
18
19import com.android.internal.annotations.VisibleForTesting;
20import com.android.server.wifi.ByteBufferReader;
21
22import java.net.ProtocolException;
23import java.nio.BufferUnderflowException;
24import java.nio.ByteBuffer;
25import java.nio.ByteOrder;
26
27/**
28 * Factory to build a collection of 802.11u ANQP elements from a byte buffer.
29 */
30public class ANQPParser {
31    /**
32     * The OI value for Hotspot 2.0 ANQP-element.
33     */
34    @VisibleForTesting
35    public static final int VENDOR_SPECIFIC_HS20_OI = 0x506F9A;
36
37    /**
38     * The Type value for Hotspot 2.0 ANQP-element.
39     */
40    @VisibleForTesting
41    public static final int VENDOR_SPECIFIC_HS20_TYPE = 0x11;
42
43    /**
44     * Parse an ANQP element from the pass-in byte buffer.
45     *
46     * Note: Each Hotspot 2.0 Release 2 element will be wrapped inside a Vendor Specific element
47     * in the ANQP response from the AP.  However, the lower layer (e.g. wpa_supplicant) should
48     * already take care of parsing those elements out of Vendor Specific elements.  To be safe,
49     * we will parse the Vendor Specific elements for non-Hotspot 2.0 Release elements or in
50     * the case they're not parsed by the lower layer.
51     *
52     * @param infoID The ANQP element type
53     * @param payload The buffer to read from
54     * @return {@link com.android.server.wifi.hotspot2.anqp.ANQPElement}
55     * @throws BufferUnderflowException
56     * @throws ProtocolException
57     */
58    public static ANQPElement parseElement(Constants.ANQPElementType infoID, ByteBuffer payload)
59            throws ProtocolException {
60        switch (infoID) {
61            case ANQPVenueName:
62                return VenueNameElement.parse(payload);
63            case ANQPRoamingConsortium:
64                return RoamingConsortiumElement.parse(payload);
65            case ANQPIPAddrAvailability:
66                return IPAddressTypeAvailabilityElement.parse(payload);
67            case ANQPNAIRealm:
68                return NAIRealmElement.parse(payload);
69            case ANQP3GPPNetwork:
70                return ThreeGPPNetworkElement.parse(payload);
71            case ANQPDomName:
72                return DomainNameElement.parse(payload);
73            case ANQPVendorSpec:
74                return parseVendorSpecificElement(payload);
75            default:
76                throw new ProtocolException("Unknown element ID: " + infoID);
77        }
78    }
79
80    /**
81     * Parse a Hotspot 2.0 Release 2 ANQP element from the pass-in byte buffer.
82     *
83     * @param infoID The ANQP element ID
84     * @param payload The buffer to read from
85     * @return {@link com.android.server.wifi.hotspot2.anqp.ANQPElement}
86     * @throws BufferUnderflowException
87     * @throws ProtocolException
88     */
89    public static ANQPElement parseHS20Element(Constants.ANQPElementType infoID,
90            ByteBuffer payload) throws ProtocolException {
91        switch (infoID) {
92            case HSFriendlyName:
93                return HSFriendlyNameElement.parse(payload);
94            case HSWANMetrics:
95                return HSWanMetricsElement.parse(payload);
96            case HSConnCapability:
97                return HSConnectionCapabilityElement.parse(payload);
98            case HSOSUProviders:
99                return RawByteElement.parse(infoID, payload);
100            default:
101                throw new ProtocolException("Unknown element ID: " + infoID);
102        }
103    }
104
105    /**
106     * Parse the ANQP vendor specific element.  Currently only supports the vendor specific
107     * element that contained Hotspot 2.0 ANQP-element.
108     *
109     * Format of a ANQP Vendor Specific element:
110     * | OI | Type | Subtype | Reserved | Payload |
111     *   3     1        1         1       variable
112     *
113     * @param payload The buffer to read from
114     * @return {@link ANQPElement}
115     * @throws BufferUnderflowException
116     * @throws ProtocolException
117     */
118    private static ANQPElement parseVendorSpecificElement(ByteBuffer payload)
119            throws ProtocolException {
120        int oi = (int) ByteBufferReader.readInteger(payload, ByteOrder.BIG_ENDIAN, 3);
121        int type = payload.get() & 0xFF;
122
123        if (oi != VENDOR_SPECIFIC_HS20_OI || type != VENDOR_SPECIFIC_HS20_TYPE) {
124            throw new ProtocolException("Unsupported vendor specific OI=" + oi + " type=" + type);
125        }
126
127        int subType = payload.get() & 0xFF;
128        Constants.ANQPElementType hs20ID = Constants.mapHS20Element(subType);
129        if (hs20ID == null) {
130            throw new ProtocolException("Unsupported subtype: " + subType);
131        }
132        payload.get();     // Skip the reserved byte
133        return parseHS20Element(hs20ID, payload);
134    }
135}
136