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 
17 package com.android.server.wifi.hotspot2.anqp;
18 
19 import com.android.internal.annotations.VisibleForTesting;
20 import com.android.server.wifi.ByteBufferReader;
21 
22 import java.net.ProtocolException;
23 import java.nio.BufferUnderflowException;
24 import java.nio.ByteBuffer;
25 import java.nio.ByteOrder;
26 
27 /**
28  * Factory to build a collection of 802.11u ANQP elements from a byte buffer.
29  */
30 public 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      */
parseElement(Constants.ANQPElementType infoID, ByteBuffer payload)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      */
parseHS20Element(Constants.ANQPElementType infoID, ByteBuffer payload)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 HSOsuProvidersElement.parse(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      */
parseVendorSpecificElement(ByteBuffer payload)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